您的位置:首页 > 其它

[poj 3207] Ikki\'s Story IV - Panda\'s Trick(2-sat or 并查集)

2014-04-14 14:41 435 查看
这是A掉的第一道2-sat题目。由于题目的特殊性,可以不需要用tarjan计算强连通图,用并查集也可以轻松搞定。

题意:一个圆上依次分布着一些点,距离不重要。然后给你一些点对,要你将这两个点连线,问存不存在一种方案,使得在所有线段都不交叉的情况下,能连完所有线段。显然线段不是在圆内连线就是在圆外连线。

样例数据如下:

4 2
0 1
3 2

4个点两条线,满足条件的方案有好2种,如下图,所以输出"panda is telling the truth..."



 
每个点有两种状态,内连或外连,所以只要建图以后判断同一个点是否既必须内连又必须外连即可。



 

容易看出两条线段何时一定会交叉。



 
 假设第一条线段是a-b,那么内部相交就是c-d以这样的形式在两边,也就是说c-d必须在圆外相连。

所以冲突的条件就是
bool conflict(int a, int b, int c, int d)
{
if(c < a && d > a && d < b) // c<a与d>a好写,千万不要遗漏d<b的条件
return true;
if(c > a && c < b && d > b)
return true;
return false;
}

找到冲突边以后建图,我用的是邻接表,要注意点对是2i,2i+1,2j,2j+1。
struct point
{
vector<int> v;
}pt[maxn*2];

for(int i = 0; i < n - 1; i++)
{

for(int j = i+1; j < n; j++)
{
if(conflict(tt[i].s, tt[i].t, tt[j].s, tt[j].t))
{
pt[i*2].v.push_back(j*2+1);
pt[j*2+1].v.push_back(i*2);
pt[i*2+1].v.push_back(j*2);
pt[j*2].v.push_back(i*2+1);
}
}
}

建图并tarjan以后,只要判断同一个点是否又得内连又得外连(无解)即可。
for(int i = 0; i < n; i++)
{
if(belong[i*2] == belong[i*2+1])
{
flag = 1;
break;
}
}

完整代码如下:
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;

#define maxn 1010
int n, m;

struct point
{
vector<int> v;
}pt[maxn*2];

struct node
{
int s;
int t;
}tt[maxn];

int index, stacktop, cnt;
int dfn[maxn*2], low[maxn*2];
int instack[maxn*2], stap[maxn*2];
int belong[maxn*2];

int min(int a, int b)
{
return a < b ? a : b;
}

bool conflict(int a, int b, int c, int d)
{
if(c < a && d > a && d < b)
return true;
if(c > a && c < b && d > b)
return true;
return false;
}

void Clear()
{
index = stacktop = cnt = 0;
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
memset(instack, false, sizeof(instack));
memset(belong, 0, sizeof(belong));
}

void tarjan(int u)
{
dfn[u] = low[u] = ++index;
instack[u] = true;
stap[++stacktop] = u;
for(int i = 0; i < pt[u].v.size(); i++)
{
if(!dfn[i])
{
tarjan(i);
low[u] = min(low[u], low[i]);
}
else if(instack[i])
{
low[u] = min(low[u], dfn[i]);
}
}
if(low[u] == dfn[u])
{
cnt++;
int v;
do
{
v = stap[stacktop--];
instack[v] = false;
belong[v] = cnt;
}while(v != u);
}
}

int main()
{
while(~scanf("%d%d", &n, &m))
{
for(int i = 0; i < m; i++)
{
scanf("%d%d", &tt[i].s, &tt[i].t);
}

for(int i = 0; i < n - 1; i++)
{
for(int j = i+1; j < n; j++)
{
if(conflict(tt[i].s, tt[i].t, tt[j].s, tt[j].t))
{
pt[i*2].v.push_back(j*2+1);
pt[j*2+1].v.push_back(i*2);
pt[i*2+1].v.push_back(j*2);
pt[j*2].v.push_back(i*2+1);
}
}
}

Clear();

for(int i = 0; i < 2*n; i++)
{
if(!dfn[i])
tarjan(i);
}
int flag = 0;
for(int i = 0; i < n; i++)
{
if(belong[i*2] == belong[i*2+1])
{
flag = 1;
break;
}
}
if(flag)
printf("the evil panda is lying again\n");
else
printf("panda is telling the truth...\n");
}
return 0;
}

另外由于这道题比较特别,矛盾边为i j和i' j'



 其实就相当于无向图,这样的话图都不用建了,直接合并i与j'、i'与j到同一集合中。最后判断i和i'是否在同一集合中(无解)即可。
#include<iostream>
using namespace std;

struct node
{
int s;
int t;
}line[600];

int father[2010];

bool judge(int a, int b, int c, int d)
{
if(c < b && c > a && d > b)
return true;
if(c < a && d > a && d < b)
return true;
return false;
}

int find(int x)
{
if(x == father[x])
return x;
return father[x] = find(father[x]);
}

int main()
{
int n, m;
int x, y;
while(~scanf("%d%d", &n, &m))
{
for(int i = 0; i < m; i++)
{
scanf("%d%d", &line[i].s, &line[i].t);
}
for(int i = 0; i < n; i++)
father[i] = i;
for(int i = 0; i < m-1; i++)
{
for(int j = i+1; j < m; j++)
{
if(judge(line[i].s, line[i].t, line[j].s, line[j].t))
{
x = find(2*i);
y = find(2*j+1);
father[x] = y;

x = find(2*i+1);
y = find(2*j);
father[x] = y;
}
}
}
int flag = 0;
for(int i = 0; i < m; i++)
{
if(find(2*i) == find(2*i+1))
{
flag = 1;
break;
}
}
if(flag)
printf("the evil panda is lying again\n");
else
printf("panda is telling the truth...\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: