题意:
连接圆周上标号 [ 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");
}