POJ 3207 Ikki's Story IV - Panda's Trick(2-SAT)
http://poj.org/problem?id=3207
题意:
平面上有一个圆,圆上有n个点(分别编号0-n-1,按顺序在圆上排列),现在要对着n个点连接m条线,这m条线的两个端点已经给出了(但是没说这些先是连内圈还是连外圈),这个线可以从圆内连或从圆外连.且任意一个点最多只作为一条线的端点.要求任意两条线不相交,问你是否可能?
分析:
由于题目中需要连接的边都确定给出了,我们可以根据任意两线i与j的端点推断出当i与j同时在圆内或圆外是否一定会相交。
如果边i与j同在圆的一侧必定相交,那么边i与边j必然一个在圆内,一个在圆外,所以有如下结论:
i在圆内或j在圆内 且 i在圆外或j在圆外 (想想为什么)
那么如何判断边i(ui,vi) 与 j(uj,vj) 在圆同一侧时一定会相交呢?
由于这里端点是在圆上的,所以如果j边的两点中,一点处于i边两端点的ui顺时针走向vi的圆弧内,一点处于i边两端点的vi顺时针走向ui的圆弧内,那么i与j在圆同一侧时一定相交。否则一定不想交。(自己画图验证一下)
AC代码:
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=1000+10;
const int maxm=500+10;
struct Edge
{
int u,v;
Edge(){}
Edge(int u,int v):u(u),v(v){}
}edges[maxm];
int cnt;
struct TwoSAT
{
int n;
vector<int> G[maxm*2];
int S[maxm*2],c;
bool mark[maxm*2];
bool dfs(int x)
{
if(mark[x]) return true;
if(mark[x^1]) return false;
mark[x]=true;
S[c++]=x;
for(int i=0;i<G[x].size();i++)
if(!dfs(G[x][i])) return false;
return true;
}
void init(int n)
{
this->n=n;
for(int i=0;i<n*2;i++) G[i].clear();
memset(mark,0,sizeof(mark));
}
void add_clause(int x,int xval,int y,int yval)
{
x=x*2+xval;
y=y*2+yval;
G[x^1].push_back(y);
G[y^1].push_back(x);
}
bool solve()
{
for(int i=0;i<2*n;i+=2)if(!mark[i] && !mark[i+1])
{
c=0;
if(!dfs(i))
{
while(c>0) mark[S[--c]]=false;
if(!dfs(i+1)) return false;
}
}
return true;
}
}TS;
int main()
{
int n,m;
scanf("%d%d",&n,&m);
cnt=0;
for(int i=0;i<m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
if(u>v) swap(u,v);
edges[cnt++]=Edge(u,v);
}
TS.init(m);
for(int i=0;i<m;i++)
for(int j=i+1;j<m;j++)
if( (edges[i].u<=edges[j].u&&edges[i].v>=edges[j].u&&edges[i].v<=edges[j].v) ||
(edges[i].u<=edges[j].v&&edges[i].v>=edges[j].v&&edges[i].u>=edges[j].u) )
{
TS.add_clause(i,0,j,0);
TS.add_clause(i,1,j,1);
}
if(TS.solve()) printf("panda is telling the truth...\n");
else printf("the evil panda is lying again\n");
return 0;
}