【整理】2-SAT
2-satisfiability,我们一般将其缩写为 2-sat。
了解全名有助于我们对这个算法的理解。 百度翻译:‘satisfiability’---“可满足性,适定性”。
由于SAT问题目前是NP问题,所以自然有最大化满足性问题———MAX-SAT。
然后也有最基本的问题,生存or死亡,嫁给我or吃屎———2-SAT。
如果能解决k-sat问题,那么他一定会火,毕竟没有很好的算法去解决,目前我们研究得更多是2-sat。
之前做的两个2-sat题:nmphy的2-sat。第三个是今天做的,整理一下。
-----------------------------------------------------我是分界线----------------------------------------------------------------
浅谈2-sat:(假设读者已经知道了2sat的原理,只是有时会乱,不知道把谁作为点,谁作为边,不知道怎么建图是好)
一般会有两个或者多个限制,要选择其中一个作为不相容限制。
然后其他的条件作为有向图,然后判环:
【关键】:整个算法转化成图的关键就是找好对象,判断出哪个作为不相容限制。
如果不相容限制的两个子都不能满足,那么结果为false。
【不相容限制】:n个被选择,每个是‘真’or‘假’,代表二者不能同时存在。
【选择限制】:m个要求,一般牵涉到两个不相容限制。
判断是哪种限制:不相容限制再每个集合都存在,而选择限制不是。
【例一】
(HDU1814):题目大意:一国有n个党派,每个党派在议会中都有2个代表,现要组建和平委员会,要从每个党派在议会的代表中选出1人,一共n人组成和平委员会。已知有一些代表之间存在仇恨,也就是说他们不能同时被选为和平委员会的成员,现要你判断满足要求的和平委员会能否创立?如果能,请任意给出一种方案。
【不相容限制】 是每个党派的代表,设为1,2。去了一个,另一个就不能去,每个党派(集合)都存在这样的关系。
【选择限制】 是存在仇恨的代表,并不是所有党派或者代表都存在这样的关系。
附上代码和注释
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <vector>
using namespace std;
#define R 1//red 为ok
#define B 2//black 访问过但不ok
#define W 0//white 待染色
const int maxn = ;
vector<int>G[maxn];
int cnt,col[maxn],ans[maxn],n,m;
bool dfs(int u)
{
if (col[u] == B) return false;
if (col[u] == R) return true;
col[u] = R;col[u^] = B;ans[cnt++]=u;//记录染了哪些,以便失败后把颜色改回来
for(int i=;i<G[u].size();i++)
if (!dfs(G[u][i])) return false;
return true;
}
bool _solve()
{
int i, j;
memset(col,,sizeof(col));
for (i=; i<n; i++){
if (col[i]) continue;
cnt=;
if (!dfs(i)){
for (j=;j<cnt;j++){
col[ans[j]]=W;//漂白
col[ans[j]^]=W;//漂白
}
if (!dfs(i^)) return false;//2-sat失败
}
}
return true;
}
int main()
{
int i,a,b;
while (~scanf("%d %d",&n, &m)){
n<<=;
for(i=;i<=n;i++) G[i].clear();
while (m--){
scanf("%d %d",&a, &b);
a--;b--;
G[a].push_back(b^);
G[b].push_back(a^);
}
if (_solve()){
for (i=; i<n; i++)
if(col[i] == R)
printf("%d\n",i+);
}
else printf("NIE\n");
}
return ;
}
【例二】
(HDU1824):集训是辛苦的,道路是坎坷的,休息还是必须的。经过一段时间的训练,lcy决定让大家回家放松一下,但是训练还是得照常进行,lcy想出了如下回家规定,每一个队(三人一队)或者队长留下或者其余两名队员同时留下;每一对队员,如果队员A留下,则队员B必须回家休息下,或者B留下,A回家。由于今年集训队人数突破往年同期最高记录,管理难度相当大,lcy也不知道自己的决定是否可行,所以这个难题就交给你了,呵呵,好处嘛~,免费**漂流一日。
【不相容限制】 对于每个人,留或者去,二选一;
【选择限制】 对于一对人,二留一;对于一队人,队员或队长不能同时离开。
®:例二有三个限制,如果没有选择好哪个是不相容限制,很可能给作图造成困难,最后爆炸。
(每次先选小的一个。保证了字典序最小)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <vector>
using namespace std;
#define R 1
#define B 2
#define W 0
const int maxn = ;
vector<int>G[maxn];
int cnt,col[maxn],ans[maxn],n,m;
bool _dfs(int u)
{
if (col[u] == B) return false;
if (col[u] == R) return true;
col[u] = R;col[u^] = B;ans[cnt++]=u;
for(int i=;i<G[u].size();i++)
if (!_dfs(G[u][i])) return false;
return true;
}
bool _solve()
{
int i, j;
memset(col,,sizeof(col));
for (i=; i<n*; i++){
if (col[i]) continue;
cnt=;
if (!_dfs(i)){
for (j=;j<cnt;j++){
col[ans[j]]=W;
col[ans[j]^]=W;
}
if (!_dfs(i^)) return false;
}
}
return true;
}
int main()
{
int i,a,b,c;
while (~scanf("%d %d",&n, &m)){
for(i=;i<n*;i++) G[i].clear();
for(i=;i<=n;i++){
scanf("%d%d%d",&a,&b,&c);
a*=;b*=;c*=;
G[a^].push_back(b);
G[a^].push_back(c);
G[b^].push_back(a);
G[c^].push_back(a);
}
for(i=;i<=m;i++){
scanf("%d%d",&a,&b);
a*=;b*=;
G[a].push_back(b^);
G[b].push_back(a^);
}
if (_solve()) printf("yes\n");
else printf("no\n");
}
return ;
}
【例三】
(hiho1467):有一场音乐会,分为上午、下午两场进行,主办方指定了n首歌让乐队进行演唱。每首歌只会被演唱一次,要么在上午要么在下午。参加音乐会的嘉宾们对于歌曲的演唱时间有一些要求。具体来说,每位嘉宾会指定两首歌曲的演唱时间(上午或者下午)。如果最后实际的演出安排中,两首歌都没有达到嘉宾的要求,那么嘉宾就会对音乐节不滿意。如嘉宾A的要求是上午《我的滑板鞋》和下午《忐忑》,而最后的演出中上午没有《我的滑板鞋》只有《忐忑》,下午没有《忐忑》只有《我的滑板鞋》,那么嘉宾A是不满意的。
音乐节主办方自然希望使所有嘉宾满意,但主办方后来发现有可能不存在一种歌曲的安排方案满足所有嘉宾,所以他们希望你判断一下这种情况是否会发生。
【不相容限制】 对于每首歌,上午或者下午,二选一。(没有不相容对应的点。拆点,假设上午是i,下午是i+n)
【选择限制】 对于每个观众,至少要完成一个要求。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
vector<int>G[];
int n,m,col[],ans[],cnt;
const int M=;
const int H=;
const int W=;
int read()
{
char c=getchar();
int s,a=;
while(c!='h'&&c!='m') c=getchar();
if(c=='h') a=n;
scanf("%d",&s);
return s+a;
}
int controt(int u)
{
if(u>n) return u-n;
return u+n;
}
bool bfs(int u)
{
if(col[u]==M) return true;
if(col[u]==H) return false;
col[u]=M;col[controt(u)]=H;ans[++cnt]=u;
for(int i=;i<G[u].size();i++)
if(!bfs(G[u][i])) return false;
return true;
}
bool find()
{
memset(col,,sizeof(col));
for(int i=;i<=n+n;i++){
if(col[i]) continue;
cnt=;
if(!bfs(i)){
for(int j=;j<=cnt;j++) {
col[ans[j]]=W;
col[controt(ans[j])]=W;
}
cnt=;
if(!bfs(controt(i))) return false;
}
}
return true;
}
int main()
{
int i,T,u,v;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(i=;i<=n+n;i++) G[i].clear();
for(i=;i<=m;i++){
u=read();
v=read();
G[u].push_back(controt(v));
G[v].push_back(controt(u));
}
if(find()) printf("GOOD\n");
else printf("BAD\n");
}
return ;
}
【例四】
(hdu1815):有n个牛棚, 还有两个中转站S1和S2, S1和S2用一条路连接起来。 为了使得任意牛棚两个都可以有道路联通,现在要让每个牛棚都连接一条路到S1或者S2。有a对牛棚互相有仇恨,所以不能让他们的路连接到同一个中转站。还有b对牛棚互相喜欢,所以他们的路必须连到同一个中专站。道路的长度是两点的曼哈顿距离。问最小的任意两牛棚间的距离中的最大值是多少?
【二分】假定ans=x
【不相容限制】 a,b选择特定的的S,若不满足dis<=x,则相排斥。
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=;
const int B=;
const int R=;
const int W=;
vector<int>G[maxn];
vector<int>G2[maxn];
int dis[][maxn],Dis;//Dis是S点,T点
int x,y,x1,y1,x2,y2,a,b,n,ans;
int col[maxn],q[maxn],num;
void init()
{
for(int i=;i<=*n;i++) G[i].clear();
for(int i=;i<=*n;i++) G2[i].clear();
ans=-;
}
void scan()
{
int i;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
Dis=abs(x1-x2)+abs(y1-y2);
for(i=;i<=n;i++){
scanf("%d%d",&x,&y);
dis[][i]=abs(x-x1)+abs(y-y1);
dis[][i]=abs(x-x2)+abs(y-y2);
}
for(i=;i<=a;i++){
scanf("%d%d",&x,&y);
G[x].push_back(y+n);
G[x+n].push_back(y);
G[y].push_back(x+n);
G[y+n].push_back(x);
}
for(i=;i<=b;i++){
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
G[x+n].push_back(y+n);
G[y+n].push_back(x+n);
} }
bool dfs(int u)
{
if(col[u]==R) return false;
if(col[u]==B) return true;
col[u]=B;
col[u>n?u-n:u+n]=R;
q[++num]=u;
for(int i=;i<G[u].size();i++) if(!dfs(G[u][i])) return false;
for(int i=;i<G2[u].size();i++) if(!dfs(G2[u][i])) return false;
return true;
}
bool check(int x)
{
int i,j;
for(i=;i<=n;i++) if(dis[][i]>x&&dis[][i]>x) return false;
for(i=;i<=*n;i++) G2[i].clear();
for(i=;i<=*n;i++) col[i]=;
for(i=;i<=n;i++)
for(j=i+;j<=n;j++){
int d1=dis[][i]+dis[][j];
int d2=dis[][i]+dis[][j]+Dis;
int d3=dis[][i]+dis[][j];
int d4=dis[][i]+dis[][j]+Dis;
if(d1>x&&d2>x&&d3>x&&d4>x) return false;
if(d1>x){
G2[i].push_back(j+n);
G2[j].push_back(i+n);
}
if(d2>x){
G2[i].push_back(j);
G2[j+n].push_back(i+n);
}
if(d3>x){
G2[i+n].push_back(j);
G2[j+n].push_back(i);
}
if(d4>x){
G2[i+n].push_back(j+n);
G2[j].push_back(i);
}
}
for(i=;i<=*n;i++){
if(col[i]) continue;
num=;
if(!dfs(i)){
for(j=;j<=num;j++) {
col[q[j]>n?q[j]-n:q[j]+n]=W;
col[q[j]]=W;
}
if(!dfs(i>n?i-n:i+n)) return false;
}
}
return true;
}
int main()
{
while(~scanf("%d%d%d",&n,&a,&b)){
init();
int L=,R=;
scan();
while(L<=R){
int mid=(L+R)>>;
if(check(mid)){ ans=mid;R=mid-;}
else L=mid+;
}
printf("%d\n",ans);
}
return ;
}
本文仅阐述如何寻找不相容限制,然后去建图。
所以代码的优化在这里没有提及,用【缩点】和【拓扑】的优化将在下文补充。
如果你对2-sat有什么新的认识,或者疑问,请留言额。待续。。。
【整理】2-SAT的更多相关文章
- shell常用命令归类整理
shell 命令整理 bash shell 含有许多功能,因此有许多可用的命令:本文档仅罗列了一些常用命令及其使用频率较高的参数.#本文档仅罗列了一些常用命令及其使用频率较高的参数.#vers ...
- Docker学习笔记整理
Docker接触有一段时间了,但是对于Docker的使用可以说是一点不会.现在要在Docker上部署基于Angular开发的页面.只能一点点积累查找的资料,顺手整理一下,方便后面的回顾. 其中用到的资 ...
- 前端必会html知识整理
1.浏览器内核: 1.ie:trident(三叉戟)内核 2.firefox:gecko(壁虎)内核 3.safari:webkit(浏览器核心)内核 ...
- (整理)ubuntu 的 相关知识(来自 鸟哥的私房菜)
1. Linux 文件权限概念 $ ls 察看文件的指令 $ ls -al 出所有的文件详细的权限与属性 (包含隐藏档,就是文件名第一个字符为『 . 』的文件) 在你第一次以root身份登入Linux ...
- MATLAB中文论坛帖子整理(GUI)
MATLAB中文论坛帖子整理(GUI) 目 录 1.GUI新手之——教你读懂GUI的M文件... 10 2.GUI程序中改变current directory引起的问题... 15 3.GUI中 ...
- 笔记整理--玩转robots协议
玩转robots协议 -- 其他 -- IT技术博客大学习 -- 共学习 共进步! - Google Chrome (2013/7/14 20:24:07) 玩转robots协议 2013年2月8日北 ...
- python 基础部分重点复习整理--从意识那天开始进阶--已结
pythonic 风格编码 入门python好博客 进阶大纲 有趣的灵魂 老齐的教程 老齐还整理了很多精华 听说 fluent python + pro python 这两本书还不错! 元组三种遍历, ...
- [转]开源日志库<log4cplus+VS2008使用>整理
转 开源日志库<log4cplus+VS2008使用>整理 转http://pyhcx.blog.51cto.com/713166/143549 一.简介 log4cplus是C+ ...
- JS知识点整理(二)
前言 这是对平时的一些读书笔记和理解进行整理的第二部分,第一部分请前往:JS知识点整理(一).本文包含一些易混淆.遗漏的知识点,也会配上一些例子,也许不是很完整,也许还会有点杂,但也许会有你需要的,后 ...
- 整理一些JavaScript时间处理扩展函数
在JavaScript中,时间处理是经常需要用到的.最近想要慢慢建立自己的代码库,整理了几个之前用到的js处理时间的函数,发出来跟大家分享一下,以后的使用中会不断增加和修改代码库. 把字符串转换为日期 ...
随机推荐
- 解释python中的help()和dir()函数
help函数是一个内置函数,用于查看函数或模块用途的详细说明 import copy print(help(copy.copy)) Help on function copy in module co ...
- pyhton3 logging模块
1.简单的将日志打印到屏幕 import logging logging.debug('This is debug message')logging.info('This is info mess ...
- iOS 什么是函数式编程
前言:当前只做理解性的常规背书,根据不断深入学习会不断丰富解读内容,欢迎评论提意见 函数式编程:Functional Programming 1 基本解释: 函数式编程 是一种思维模式,一种编程思想, ...
- 微服务架构~BFF和网关是如何演化出来的
介绍 BFF(Backend for Frontend)和网关Gateway是微服务架构中的两个重要概念,这两个概念相对比较新,有些开发人员甚至是架构师都不甚理解. 本文用假想的公司案例+图示的方式, ...
- excel中如何取消自动超链接?
最近做的表格有点多,年终述职也到了.总有一些地方生疏了,幸好还有点小印象.记录下来,以后可以回来看看. 方法一 适合单个链接的取消 1 输入网址后,按回车键确认,快捷键ctrl+z,即可取消,这种不好 ...
- 前段时间说了AssetBundle打包,先设置AssetLabels,再执行打包,但是这样有个弊端就是所有设置了AssetLabels的资源都会打包,这次说说不设置AssetLabels,该如何打包AssetBundle
BuildPipeline.BuildAssetBundles() 这个函数,有多个重载,一个不用AssetBundleBuild数组,一个需要,如果设置了AssetLabels,那么这时候是不需要的 ...
- Threalocal的使用及其原理
虽然现在可以说很多程序员会用ThreadLocal,但是我相信大多数程序员还不知道ThreadLocal,而使用ThreadLocal的程序员大多只是知道其然而不知其所以然,因此,使用ThreadLo ...
- centOS最小化安装后网络连接问题
编辑配置文件 vi /etc/sysconfig/network-script/ifcfg-eth0 修改此行重启后即可 ONBOOT="yes" #修改为 ...
- Windows批量添加和删除IP
随着天气变冷了,好多小伙伴都开始变懒了,都想用最快的方式完成任务 下面给大家介绍一下Windows批量添加和删除IP的办法 (1)批量添加IP 直接在CMD下边运行下边命令. for /l %i in ...
- IOS 发布被拒 3.2 f
Dear Developer, We have determined that your Apple Developer Program membership, or another membersh ...