[POJ 3207] Ikki's Story IV

来源:互联网 发布:光猫端口23失败 编辑:程序博客网 时间:2024/05/19 22:58

题目传送门:【POJ 3207】

题目大意: Liympanda 有一个魔法圆环,他把这个圆环放在一个平面上。圆环上有 n 个点:0,1,2,…,n-1。Evil panda 声称他要连接圆环上的 m 对点。为了连接两个点,Liympanda 需要把这条连线整个地放在圆的内部,或者是放在圆的外部。Liympanda 告诉你,任意两条(圆外和圆内的)连线都不能相交,每个点都最多只会连上一条线。现在他想让你帮助他回答这个问题。

输入恰好为一组数据。第一行两个整数 n , m(n ≤ 1000 , m ≤ 500),代表有 n 个点和 m 条连线。接下来 m 行,每行两个数 ai , bi,表示第 i 条线连接的两个点。如果连线两两不相交,那么输出“panda is telling the truth…”;否则输出“the evil panda is lying again”。


题目分析:
由题,对于每条连线,都只存在两种状态:这条线在圆内,或者是这条线在圆外;同时,每一条线只能选择其中的一种状态。因此,我们可以将每一条线转化为两个点,分别表示“圆内”和“圆外”。那么很显然,我们可以使用 2-SAT 来做。
对于一些情况,当两条线互相包夹的时候(例如,第 1 条线连的 1,4,第 2 条线连的 2,5),此时这两条线必须为一条在圆内,另一条在圆外,否则就会相交。所以对于这种情况,我们可以按照 2-SAT 的方式去连边来表示它们的冲突情况(1内->2外,2内->1外,等等)。然后跑一遍 Tarjan 看有没有既为圆内又为圆外的点即可。

对于上面的样例,如果还存在第 3 条线(3,6),那么此时一定会相交,因为跑完 Tarjan 后的图就有“既为圆内又为圆外的点”的情况。

下面附上代码:

[cpp] view plain copy
print?
  1. #include<cstdio>  
  2. #include<cstring>  
  3. #include<iostream>  
  4. #include<algorithm>  
  5. #include<stack>  
  6. using namespace std;  
  7. const int MX = 1005;  
  8.   
  9. struct Edge{  
  10.     int to,next;  
  11. }edge[MX * MX * 2];  
  12. struct Point{                                   //表示每一对点的编号   
  13.     int a,b;  
  14. }pt[MX];  
  15. int n,m;  
  16. int head[MX],now = 0,cnt = 0,_index = 0,dfn[MX],low[MX],belong[MX];  
  17. bool ins[MX];  
  18. stack<int> s;  
  19.   
  20. void adde(int u,int v){  
  21.     edge[++now].to = v;  
  22.     edge[now].next = head[u];  
  23.     head[u] = now;  
  24. }  
  25.   
  26. void tarjan(int u){  
  27.     dfn[u] = low[u] = ++_index;  
  28.     s.push(u);  
  29.     ins[u] = true;  
  30.     for (int i = head[u];i;i = edge[i].next){  
  31.         int v = edge[i].to;  
  32.         if (!dfn[v]){  
  33.             tarjan(v);  
  34.             if (low[v] < low[u]){  
  35.                 low[u] = low[v];  
  36.             }  
  37.         } else if (ins[v] && dfn[v] < low[u]){  
  38.             low[u] = dfn[v];  
  39.         }  
  40.     }  
  41.     if (low[u] == dfn[u]){  
  42.         ++cnt;  
  43.         int tmp;  
  44.         do {  
  45.             tmp = s.top();  
  46.             s.pop();  
  47.             ins[tmp] = false;  
  48.             belong[tmp] = cnt;  
  49.         }while (tmp != u);  
  50.     }  
  51. }  
  52.   
  53. int main(){  
  54.     int a,b;  
  55.     cin>>n>>m;  
  56.     for (int i = 1;i <= m;i++){  
  57.         cin>>a>>b;  
  58.         ++a;++b;                                //swap和+1,方便之后处理   
  59.         if (a>b) swap(a,b);  
  60.         pt[i].a = a;  
  61.         pt[i].b = b;  
  62.     }  
  63.     for (int i = 1;i <= m;i++){  
  64.         for (int j = i + 1;j <= m;j++){  
  65.             if ((pt[i].a < pt[j].a && pt[i].b > pt[j].a && pt[i].b < pt[j].b)  
  66.               || (pt[i].a > pt[j].a && pt[i].a < pt[j].b && pt[i].b > pt[j].b)){  
  67.                 //如果两个点互相包夹(如上),说明它们必须为一条边在圆内,一条边在圆外   
  68.                 //按照2-SAT的对应关系建边   
  69.                 adde(i,j + m);  
  70.                 adde(j,i + m);  
  71.                 adde(i + m,j);  
  72.                 adde(j + m,i);  
  73.             }  
  74.         }  
  75.     }  
  76.     for (int i = 1;i <= 2 * m;i++){  
  77.         if (!dfn[i]) tarjan(i);  
  78.     }  
  79.     bool reach = true;  
  80.     for (int i = 1;i <= m;i++){  
  81.         if (belong[i] == belong[i + m]){        //表示这条边既在圆内,又在圆外,矛盾   
  82.             reach = false;  
  83.             break;  
  84.         }  
  85.     }  
  86.     if (reach) printf(“panda is telling the truth…”);  
  87.     else printf(“the evil panda is lying again”);  
  88.     return 0;  
  89. }  
原创粉丝点击