题意:
连接圆周上标号 [ 0 .. n-1 ] 的点, 连接线可以放在圆内或圆外,但不能交叉(叠放). 给出点的个数n和相连的边(点对).判断是否可行.
思路:
2-SAT简单版:
将每条连接线看成两个点, 分别表示在圆内, 圆外. 实际中这两个点能且只能取其一.
枚举两根连接线的所有组合情况, 根据连接线所连两点的标号判断二者是否回相交.
若会, 则必然一个在圆内, 一个在圆外. 这两条连接线的状态是绑定的, 据此连线(无向图).
Tarjan求出强连通分量,
枚举每一条连接线, 判断它的两种状态是否在同一个强连通分量中. 若是, 矛盾. 否则可行.
**这最后一步是没有优化的(相对于尚未看懂的<<由对称性解2-SAT问题>>...)
#include <cstdio> #include <cstring> #include <stack> #include <algorithm> using namespace std; const int MAXN = 1005; const int MAXE = 505; struct pool { int v,pre; }p[MAXE*MAXE]; int edge[MAXE][2],n,m; int head[MAXE<<1],num,dfn[MAXE<<1],low[MAXE<<1],id[MAXE<<1],size,Index; bool used[MAXE<<1]; stack<int> s; void add(int u, int v) { p[++num].v = v; p[num].pre = head[u]; head[u] = num; } void Tarjan(int u) { dfn[u] = low[u] = ++Index; used[u] = true; s.push(u); for(int tmp=head[u],k;k=p[tmp].v,tmp;tmp=p[tmp].pre) { if(!dfn[k]) { Tarjan(k); low[u] = min(low[u],low[k]); } else if(used[k]) low[u] = min(low[u],dfn[k]); } if(dfn[u] == low[u]) { size++; int k; do { k = s.top();s.pop(); used[k] = false; id[k] = size; }while(u!=k); } } int main() { scanf("%d %d",&n,&m); for(int i = 0,u,v;i<m;i++) { scanf("%d %d",&u,&v); edge[i][0] = u,edge[i][1] = v; if(edge[i][0]>edge[i][1]) swap(edge[i][0],edge[i][1]); } for(int i=0;i<m;i++) { for(int j=i+1;j<m;j++) { if( (edge[i][0]<edge[j][0] && edge[i][1]<edge[j][1] && edge[i][1]>edge[j][0]) || (edge[i][0]>edge[j][0] && edge[i][1]>edge[j][1] && edge[i][1]<edge[j][0]) ) { add(i,j+m); add(j,i+m); add(i+m,j); add(j+m,i); } } } for(int i=0;i<m<<1;i++)//每一种状态都要试一遍 { if(!dfn[i]) Tarjan(i); } bool flag = true; for(int i=0;i<m;i++) if(id[i]==id[i+m]) { flag = false; //printf("%d and %d\n",edge[i][0],edge[i][1]); break; } if(flag) printf("panda is telling the truth...\n"); else printf("the evil panda is lying again\n"); }