开始学习2-sat的姿势
http://blog.csdn.net/jarjingx/article/details/8521690
这篇文章写的真好
算法的过程如下:
构图
更具体的后面再说
缩点
Tarjan算法缩点,将所有的边反过来( 为什么?后面有嗯 )
判可行
枚举集合的两个元素,看其是否处于不同的块内,若否的话则给出不可行信息
记录矛盾
这里所说的矛盾不是题中给出的两个人之间有仇恨,那样的边是实际存在,我们这里说的矛盾是指若两个块分别含有两个对立节点,也就是说一个集合的两个元素分布在了两个不同的块中,那么这两个块就是矛盾的,即不可能同时被选择,这样一种矛盾关系是不存在于边中的,是不依赖于输入的数据的,我们要找到与一个块对立的块,并把它们保存下来。
拓扑排序
将缩点后的图进行拓扑排序(排的是块而不是节点)
构造方案
按照拓扑序列的顺序,依次访问所有块,若一个块未被标记,将其标记为“选择”,不传递“选择”标记,将被选块的对立块标记为“不选择”,将其“不选择”标记沿着边正向传递。( 不是逆着边么?哼,图已经被反过来了,你到底有没有认真看呐!囧 )
本题建图判断即可
By:大奕哥
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; const int N=1e5+5; int low[N],vis[N],dfn[N],n,m,cnt,ccnt,num,head[N],s[N],top,col[N]; struct node{ int x,y; }p[N]; struct edge { int to,nex; }e[N<<2]; void add(int x,int y) { e[++ccnt].to=y;e[ccnt].nex=head[x];head[x]=ccnt; } void dfs(int x) { s[++top]=x; vis[x]=1;dfn[x]=low[x]=++cnt; for(int i=head[x];i;i=e[i].nex) { int y=e[i].to; if(!dfn[y]){ dfs(y); low[x]=min(low[y],low[x]); } else if(vis[y]){ low[x]=min(low[x],dfn[y]); } } if(low[x]==dfn[x]) { num++; while(s[top+1]!=x){ int a=s[top--]; vis[a]=0; col[a]=num; } } return; } void tarjan() { cnt=num=top=0; memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(vis,0,sizeof(vis)); memset(col,0,sizeof(col)); for(int i=1;i<=m*2;++i) if(!dfn[i])dfs(i); } bool solve() { tarjan(); for(int i=1;i<=m;++i)if(col[i]==col[i+m])return 0; return 1; } int main() { while(~scanf("%d%d",&n,&m)) { ccnt=0;memset(head,0,sizeof(head));int x,y; for(int i=1;i<=m;++i) { scanf("%d%d",&x,&y); if(x>y)swap(x,y); p[i].x=x;p[i].y=y; } for(int i=1;i<=m;++i) for(int j=i+1;j<=m;++j){ if(p[j].x>=p[i].x&&p[j].x<=p[i].y&&p[i].y<=p[j].y|| p[j].x<=p[i].x&&p[i].x<=p[j].y&&p[j].y<=p[i].y) { add(i,j+m);add(i+m,j); add(j,i+m);add(j+m,i); } } if(solve())puts("panda is telling the truth..."); else puts("the evil panda is lying again"); } return 0; }