本文原创于 2014-02-12 09:26。 今复习之用,有新体会,故重新编辑。
2014-02-12 09:26:
2-sat之第二斩!昨天看了半天论文(赵爽的和 昱的),终于看明白了!好激动有木有!终于理解了赵爽的每一句话!并且用了200+行代码实现,A了!具体过程我是敲了帮天的代码啊!!!不容易啊!步骤如下:
把相关问题编号为01 23 45....,(每个编号为一个命题)奇数为取,偶数不取,那么相邻俩个互逆,于是根据具体情况(check)一下,建立图,tarjan判断有无解,然后顺便再缩点,重新建图(逆图),在对新图拓扑,仔细阅读下面赵爽的话:理解每一句:
如果没有产生矛盾,我们就可以把处在同一个强连通分量中的点和边缩成一个点,得到
新的有向图G。然后,我们把G中的所有弧反向,得到图G ′ ′ ′′。
现在我们观察 。由于已经进行了缩点的操作,因此 G′′ G′′中一定不存在圈,也就是说,
具有拓扑结构。 G′′
我们把G中所有顶点置为“未着色”。按照拓扑顺序重复下面的操作: ′′ 是啊,先对新图(逆的)拓扑,保存起来,然后开始染色,对每个染成“不选”的还要对其子孙也不选 择,(再次dfs。。。无奈),废了半天啊!!!!下面第一段代码便是!!
1、 选择第一个未着色的顶点x。把x染成红色。
2、 把所有与x矛盾的顶点 (如果存在bb yjjB ∈ ,且b属于 j
x代表的强连
通分量, j
b 属于 代表的强连通分量,那么 y x和 就是互相矛盾的顶点)
及其子孙全部全部染成蓝色。
y
3、 重复操作1和2,直到不存在未着色的点为止。此时,G′′中被染成红色的
点在图G中对应的顶点集合,就对应着该2-SAT的一组解。
后来在大牛交流中,发现无需拓扑啊!白痴啊!尽在眼前还去自己写什么??!!了解到:每个强连通分量都是在它的所有后继强连通分量被求出之后求得的。因此,如果将同一强连通分量收缩为一个结点而构成一个有向无环图,这些强连通分量被求出的顺序是这一新图的逆拓扑序!!!!
不用再次新图拓扑啊!!!何必多此一举!于是来了第二个代码!!
还没完???的确,染色?大牛证明了(现在证明看来也很容易的),无须如此!直接tarjan即可!详见代码三!!又简单了许多啊!从此,2-sat输出方案,哦?不用怕!!!!so easy!
继续刷几题,练练新剑!
今//三种代码:一次比一次简单,第一次完全按论文进行模拟的,比较繁琐,但是思路清晰,包括俩次建图+拓扑+染色+tarjan+dfs,
建图是关键,每次添加的边要互为假言易位式(一对),最后一种方法最妙,以后都用这样的方法,简单又快捷;
该题题意:某一天结婚的人特别多但是主持婚礼的神父只有一个。婚礼时间从s开始到e结束,神父必须在s到s+d或者e-d到e这段时间内在。给定了n个婚礼的s,e,d,求一种方案能使得神父主持所有的婚礼。
思路:建图简单,数据处理一下,按编号保存,之后:遍历点,取矛盾的点添加假言易位边,缩点(同一个SCC中必然可以互推)来判断有无解,输出方案的话,只需新图(不必真的建),每次取逆拓扑小的(scc[i]小的命题即可)(反证即可)。
#include//5340K 360MS #include #include #include #include #include using namespace std; int n;const int MAX=2001; struct points //点,01,23,45.。。相连为一对,x^1取对应点(改变奇偶性) { int from,end; }; points point[MAX]; int low[MAX];int dfn[MAX];int visited[MAX];bool is_instack[MAX];stack s; int times=0; int scc[MAX]; int numblock; int indgree[MAX]; int tuopoxuliu[MAX]; int color[MAX]; //入度,tuopo序列,染色 vector ans(MAX); //最终答案 vector >edges(MAX); //原图 vector >newgraph(MAX); //新图 vector >SCC(MAX); //保存SCC【i】含有的点 void initialize() { numblock=times=0; for(int i=0;i<2*n;i++) { tuopoxuliu[i]=color[i]=visited[i]=low[i]=dfn[i]=is_instack[i]=0; edges[i].clear(); scc[i]=-1; } } void tarjan(int u) //有向图dfs,这个不解释 { low[u]=dfn[u]=++times; is_instack[u]=1; s.push(u); int len=edges[u].size(); for(int i=0;i low[v])low[u]=low[v]; } else if(is_instack[v]&&dfn[v] b.from) //注意==号的判定!别因为这个跪了! return true; if(b.from<=a.from&&b.end>a.from) return true; return false; } bool build_graph_has_solution() //建图 { initialize(); for(int i=0;i<2*n;i++) for(int j=i+1;j<2*n;j++) { if(((i>>1)!=(j>>1))&&agst(point[i],point[j])) //有时间冲突 { if(agst(point[i],point[j^1])) //和另一个也矛盾,那么i不能选(用A->非A表示) edges[i].push_back(i^1); else { edges[i].push_back(j^1); //那么选你没我 edges[j].push_back(i^1); } } } for(int i=0;i<2*n;i++) { if(visited[i]==0) { visited[i]=1; tarjan(i); } } for(int i=0;i<2*n;i+=2) { if(scc[i]==scc[i+1]) //矛盾的点在一个SCC中, { printf("NO\n"); return false; } } return true; } void tuopu() //新图拓扑,记录拓扑序列(1-numblock)保存之 { stack sta; int count=1; for(int i=1;i<=numblock;i++) //入度点0点 if(indgree[i]==0) sta.push(i); while(!sta.empty()) { int cur=sta.top(); sta.pop(); tuopoxuliu[count++]=cur; int len4=newgraph[cur].size(); //新图,其孩子入度-- for(int i=0;i . #include#include #include #include #include using namespace std; int n;const int MAX=2001; struct points //点,01,23,45.。。相连为一对,x^1取对应点(改变奇偶性) { int from,end; }; points point[MAX]; int low[MAX];int dfn[MAX];int visited[MAX];bool is_instack[MAX];stack s; int times=0; int scc[MAX]; int numblock; int color[MAX]; //染色 vector ans(MAX); //最终答案 vector >edges(MAX); //原图 vector >newgraph(MAX); //新图 vector >SCC(MAX); //保存SCC【i】含有的点 void initialize() { numblock=times=0; for(int i=0;i<2*n;i++) { color[i]=visited[i]=low[i]=dfn[i]=is_instack[i]=0; edges[i].clear(); scc[i]=-1; } } void tarjan(int u) //有向图dfs,这个不解释 { low[u]=dfn[u]=++times; is_instack[u]=1; s.push(u); int len=edges[u].size(); for(int i=0;i low[v])low[u]=low[v]; } else if(is_instack[v]&&dfn[v] b.from) //注意==号的判定!别因为这个跪了! return true; if(b.from<=a.from&&b.end>a.from) return true; return false; } bool build_graph_has_solution() //建图 { initialize(); for(int i=0;i<2*n;i++) for(int j=i+1;j<2*n;j++) { if(((i>>1)!=(j>>1))&&agst(point[i],point[j])) //有时间冲突 { if(agst(point[i],point[j^1])) //和另一个也矛盾,那么i不能选(用A->非A表示) edges[i].push_back(i^1); else { edges[i].push_back(j^1); //那么选你没我 edges[j].push_back(i^1); } } } for(int i=0;i<2*n;i++) { if(visited[i]==0) { visited[i]=1; tarjan(i); } } for(int i=0;i<2*n;i+=2) { if(scc[i]==scc[i+1]) //矛盾的点在一个SCC中, { printf("NO\n"); return false; } } return true; } void dfs_unchoose(int u) //u及其子孙都不选 { int len5=newgraph[u].size(); for(int i=0;i
#include//无需自己拓扑!无需染色!无需重新建图! !以后不用怕了!直接秒杀! #include #include #include #include #include using namespace std; int n;const int MAX=2001; struct points //点,01,23,45.。。相连为一对,x^1取对应点(改变奇偶性) { int from,end; }; points point[MAX]; int low[MAX];int dfn[MAX];int visited[MAX];bool is_instack[MAX];stack s; int times=0; int scc[MAX]; int numblock; vector ans(MAX); //最终答案 vector >edges(MAX); //原图 void initialize() { numblock=times=0; for(int i=0;i<2*n;i++) { visited[i]=low[i]=dfn[i]=is_instack[i]=0; edges[i].clear(); scc[i]=-1; } } void tarjan(int u) //有向图dfs,这个不解释 { low[u]=dfn[u]=++times; is_instack[u]=1; s.push(u); int len=edges[u].size(); for(int i=0;i low[v])low[u]=low[v]; } else if(is_instack[v]&&dfn[v] b.from) //注意==号的判定!别因为这个跪了! return true; if(b.from<=a.from&&b.end>a.from) return true; return false; } bool build_graph_has_solution() //建图 { initialize(); for(int i=0;i<2*n;i++) for(int j=i+1;j<2*n;j++) { if(((i>>1)!=(j>>1))&&agst(point[i],point[j])) //有时间冲突 { if(agst(point[i],point[j^1])) //和另一个也矛盾,那么i不能选(用A->非A表示) edges[i].push_back(i^1); else { edges[i].push_back(j^1); //那么选你没我 edges[j