[poj 3207] Ikki\'s Story IV - Panda\'s Trick(2-sat or 并查集)
2014-04-14 14:41
435 查看
这是A掉的第一道2-sat题目。由于题目的特殊性,可以不需要用tarjan计算强连通图,用并查集也可以轻松搞定。
题意:一个圆上依次分布着一些点,距离不重要。然后给你一些点对,要你将这两个点连线,问存不存在一种方案,使得在所有线段都不交叉的情况下,能连完所有线段。显然线段不是在圆内连线就是在圆外连线。
样例数据如下:
4个点两条线,满足条件的方案有好2种,如下图,所以输出"panda is telling the truth..."
![](https://img-blog.csdn.net/20140503001702453?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFjdHJveA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
每个点有两种状态,内连或外连,所以只要建图以后判断同一个点是否既必须内连又必须外连即可。
![](https://img-blog.csdn.net/20140503001721406?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFjdHJveA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
容易看出两条线段何时一定会交叉。
![](https://img-blog.csdn.net/20140503001733265?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFjdHJveA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
假设第一条线段是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'
![](https://img-blog.csdn.net/20140503001752968?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFjdHJveA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
其实就相当于无向图,这样的话图都不用建了,直接合并i与j'、i'与j到同一集合中。最后判断i和i'是否在同一集合中(无解)即可。
题意:一个圆上依次分布着一些点,距离不重要。然后给你一些点对,要你将这两个点连线,问存不存在一种方案,使得在所有线段都不交叉的情况下,能连完所有线段。显然线段不是在圆内连线就是在圆外连线。
样例数据如下:
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; }
相关文章推荐
- POJ 3207 Ikki's Story IV - Panda's Trick 强连通分量或并查集+2sat
- poj 3207 Ikki's Story IV - Panda's Trick( 2-sat判定性问题,SCC缩点)
- poj 3207 Ikki's Story IV - Panda's Trick(2-sat)
- POJ 3207 Ikki's Story IV - Panda's Trick (2-SAT)
- Ikki's Story IV - Panda's Trick (poj 3207 2-SAT)
- POJ 3207 Ikki's Story IV - Panda's Trick(2-SAT)
- poj 3207 Ikki's Story IV - Panda's Trick
- POJ 3207 Ikki's Story IV - Panda's Trick(2-SAT判定)
- POJ 3207 Ikki's Story IV - Panda's Trick(2-sat)
- POJ 3207 Ikki's Story IV - Panda's Trick
- POJ 3207:Ikki's Story IV - Panda's Trick(2-SAT是否有解)
- POJ 3207 Ikki's Story IV - Panda's Trick 2-sat
- poj 3678 Katu Puzzle && POJ 3207 Ikki's Story IV - Panda's Trick(2-sat水题)
- POJ 3207 Ikki's Story IV - Panda's Trick(2-SAT)
- poj 3207 Ikki's Story IV - Panda's Trick(2-sat)
- [POJ 3207] Ikki's Story IV - Panda's Trick 2-SAT
- poj 3207 Ikki's Story IV - Panda's Trick【2-set】
- POJ 3207 Ikki's Story IV - Panda's Trick(2 - sat啊)
- POJ 3207 Ikki's Story IV - Panda's Trick(2-sat)
- POJ 3207 Ikki's Story IV - Panda's Trick 2-SAT