poj 3207 2-SAT问题
思路:将线段按开始点的升序排序,对线段尾节点进行判断,若存在交叉,那么这两条线段就不能同时在内或同时在外。这样将每条线段在内和在外看成两个状态i和i',i表示线段在内,i'表示线段在外。假使线段i和线段j相交,那么i和j是矛盾,且i'和j'是矛盾。
具体见代码
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<queue> #define Maxn 2010 #define Maxm Maxn*Maxn using namespace std; int vi[Maxn],head[Maxn],dfn[Maxn],low[Maxn],e,n,lab,top,num,m,id[Maxn],Stack[Maxn]; struct Edge{ int u,v,next; }edge[Maxm]; struct line{ int u,v; }p[Maxn]; void init()//初始化 { memset(vi,0,sizeof(vi)); memset(head,-1,sizeof(head)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(id,0,sizeof(id)); e=lab=top=num=0; } void add(int u,int v)//加边 { edge[e].u=u,edge[e].v=v,edge[e].next=head[u],head[u]=e++; } int cmp(line a,line b) { if(a.u==b.u) return a.v<b.v; return a.u<b.u; } void Tarjan(int u)//找出强连通分支 { int i,j,v; //cout<<u<<endl; dfn[u]=low[u]=++lab; Stack[top++]=u; vi[u]=1; for(i=head[u];i!=-1;i=edge[i].next) { v=edge[i].v; if(!dfn[v]) { Tarjan(v); low[u]=min(low[u],low[v]); } if(vi[v]) low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]) { ++num; do{ i=Stack[--top]; vi[i]=0; id[i]=num; }while(i!=u); } } int solve() { int i,j; for(i=1;i<=m*2;i++) if(!dfn[i]) Tarjan(i); for(i=1;i<=m;i++) if(id[i]==id[i+m]) return 0;//有矛盾则结束 return 1; } int cross(line a,line b) { if(a.v>b.u&&a.v<b.v) return 1; return 0; } int main() { int i,j,a,b; while(scanf("%d%d",&n,&m)!=EOF) { init(); for(i=1;i<=m;i++) { scanf("%d%d",&a,&b); p[i].u=a<b?a:b; p[i].v=a>b?a:b; } sort(p+1,p+1+m,cmp); for(i=1;i<m;i++) for(j=i+1;j<=m;j++) { if(cross(p[i],p[j]))//如果有两条线的区间相交,那么存在矛盾 { add(i,j+m); add(j,i+m); add(i+m,j); add(j+m,i); } } if(solve()) printf("panda is telling the truth...\n"); else printf("the evil panda is lying again\n"); } return 0; }