POJ 3207 Ikki's Story IV - Panda's Trick 强连通分量或并查集+2sat
2016-10-12 20:13
246 查看
标签:解题报告 图论
判断是否有两条边i,j会相交。若相交,则在图中添加四条有向边:
对强连通分量进行缩点,表示如果
怀疑太简单?缩完点的有向无环图一定有解吗?
假设无解,那必然是因为
原题见POJ 3207
有n个点围成一个圈,给出m条可以弯曲的边,使得点两两相连。这些边可以在圆内画,也可以在圆外画。问是否有可以让边不相交的画法。分析
每条边都有两种选择,画在内部或者外部。以这两种状态来标记边,共有m对状态,对第i条边,以2i,
2i+1来标记两种状态。而且这种状态必须选一种作为结果。这符合2-sat的说法。
判断是否有两条边i,j会相交。若相交,则在图中添加四条有向边:
2j+1<->2i,
2i+1<->2j,形成有向图。对于有向图中的
u->v表示选了u必须选择v。
对强连通分量进行缩点,表示如果
2i,
2i+1在同一强连通分量中,则无解。否则有解。
怀疑太简单?缩完点的有向无环图一定有解吗?
假设无解,那必然是因为
2i,
2i+1在一个连通图中。不妨设存在一个子图a,有
2i->a,
a->2i+1,则必存在边
2i<->a,
a<->2i+1,形成有环图,矛盾。
Tarjon+2sat版本代码
/*-------------------------------------------- * File Name: POJ 3207 * Author: Danliwoo * Mail: Danliwoo@outlook.com * Created Time: 2016-10-12 16:13:50 --------------------------------------------*/ #include <cstdio> #include <iostream> #include <cstring> #include <queue> #include <algorithm> #include <cmath> using namespace std; #define N 510 int n, m; struct edge { int u, v; void sc() { scanf("%d%d", &u, &v); } }p ; std::vector<int> G[N<<1]; int out[N<<1]; void add(int x, int y) { G[x].push_back(y); out[x]++; } int dt(int x) { return x == 0 ? 0 : (x > 0 ? 1 : -1); } bool cross(edge a, edge b) { return dt((a.u - b.u) * (a.v - b.u)) * dt((a.u - b.v) * (a.v - b.v)) < 0; } void make() { memset(G, 0, sizeof(G)); memset(out, 0, sizeof(out)); for(int i = 0;i < m;i++) for(int j = 0;j < i;j++) { if(cross(p[i], p[j])) { add(i<<1, j<<1|1); add(j<<1, i<<1|1); add(i<<1|1, j<<1); add(j<<1|1, i<<1); } } } int vis[N<<1], dfn[N<<1], low[N<<1], stk[N<<1], ins[N<<1], clus[N<<1]; int top, clk, cn; void Tarjon(int x) { dfn[x] = low[x] = clk++; stk[top++] = x; vis[x] = ins[x] = 1; for(int j = 0;j < G[x].size();j++) { int y = G[x][j]; if(!vis[y]) { Tarjon(y); low[x] = min(low[x], low[y]); } else if(ins[y]) { low[x] = min(low[x], dfn[y]); } } if(low[x] == dfn[x]) { do { ins[stk[top-1]] = 0; clus[stk[top-1]] = cn; top--; } while(stk[top] != x); cn++; } } bool solve() { for(int i = 0;i < m;i++) if(clus[i<<1] == clus[i<<1|1]) return false; return true; } int main() { while(~scanf("%d%d", &n, &m)) { for(int i = 0;i < m;i++) p[i].sc(); make(); memset(vis, 0, sizeof(vis)); cn = top = clk = 0; for(int i = 0;i < (m<<1);i++) if(!vis[i]) Tarjon(i); if(solve()) printf("panda is telling the truth...\n"); else printf("the evil panda is lying again\n"); } return 0; }
并查集+2sat版本
实际上,这题由于双向对称的缘故,连的都是无向边,经过缩点后即形成多个点,点与点之间若有边,则两点又被合并为一个点。用并查集也能搞缩点。/*-------------------------------------------- * File Name: POJ 3207 * Author: Danliwoo * Mail: Danliwoo@outlook.com * Created Time: 2016-10-12 17:05:44 --------------------------------------------*/ #include <cstdio> #include <iostream> #include <cstring> #include <queue> #include <algorithm> #include <cmath> using namespace std; #define N 510 int n, m; struct edge { int u, v; void sc() { scanf("%d%d", &u, &v); } }ed ; int p[N << 1]; int get(int x) { if(x != p[x]) p[x] = get(p[x]); return p[x]; } void un(int x, int y) { get(x); get(y); if(p[x] != p[y]) p[p[x]] = p[y]; } int dt(int x) { return x == 0 ? 0 : (x > 0 ? 1 : -1); } bool cross(edge a, edge b) { return dt((a.u - b.u) * (a.v - b.u)) * dt((a.u - b.v) * (a.v - b.v)) < 0; } void make() { for(int i = 0;i < 2*m;i++) p[i] = i; for(int i = 0;i < m;i++) for(int j = 0;j < i;j++) { if(cross(ed[i], ed[j])) { un(i<<1, (j<<1|1)); un(j<<1, (i<<1|1)); } } } bool solve() { for(int i = 0;i < m;i++) if(p[i<<1] == p[i<<1|1]) return false; return true; } int main() { while(~scanf("%d%d", &n, &m)) { for(int i = 0;i < m;i++) ed[i].sc(); make(); if(solve()) printf("panda is telling the truth...\n"); else printf("the evil panda is lying again\n"); } return 0; }
相关文章推荐
- [poj 3207] Ikki\'s Story IV - Panda\'s Trick(2-sat or 并查集)
- 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(2-sat简单应用)
- POJ 3207 Ikki's Story IV - Panda's Trick (POJ 六道2-SAT之一)
- Ikki's Story IV - Panda's Trick (poj 3207 2-SAT)
- poj 3207 Ikki's Story IV - Panda's Trick(2-sat)
- [2-SAT] poj 3207 Ikki's Story IV - Panda's Trick
- POJ 3207 Ikki's Story IV - Panda's Trick (POJ 六道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|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-sat
- POJ 3207 Ikki's Story IV - Panda's Trick (2-SAT)