【POJ3207】Ikki's Story IV - Panda's Trick

POJ 3207 Ikki's Story IV - Panda's Trick

liympanda, one of Ikki’s friend, likes playing games with Ikki. Today after minesweeping with Ikki and winning so many times, he is tired of such easy games and wants to play another game with Ikki.ios

liympanda has a magic circle and he puts it on a plane, there are n points on its boundary in circular border: 0, 1, 2, …, n − 1. Evil panda claims that he is connecting m pairs of points. To connect two points, liympanda either places the link entirely inside the circle or entirely outside the circle. Now liympanda tells Ikki no two links touch inside/outside the circle, except on the boundary. He wants Ikki to figure out whether this is possible…ide

Despaired at the minesweeping game just played, Ikki is totally at a loss, so he decides to write a program to help him.this

Input

The input contains exactly one test case.spa

In the test case there will be a line consisting of of two integers: n and m (n ≤ 1,000, m ≤ 500). The following m lines each contain two integers ai and bi, which denote the endpoints of the ith wire. Every point will have at most one link..net

Output

Output a line, either 「panda is telling the truth...」 or 「the evil panda is lying again」.code

Sample Input

4 2
0 1
3 2blog

Sample Output

panda is telling the truth...排序


題目大意

在一個圓上有n個點(依從從0~n-1編號)。再給定m組點,表示這兩個點要相互鏈接,鏈接的方式要麼在圈外,要麼在圈內。詢問,可否使得任意兩個連線都沒有交點(端點除外)?圖片

解決

若是存在交點,是什麼狀況?


這裏寫圖片描述




在圓內若是有交點,例如,(0,1)(2,4)的兩條連線。在圖上很顯然的就存在交點。觀察一下,那麼就是兩組連線的點依次出現。
即:Link[i].a < Link[j].a < Link[i].b < Link[j].b(a始終比b小)
那麼此時必定存在交點ci

再來看在圓外的狀況
這裏寫圖片描述




原諒我圖畫得奇醜無比(然而我已經盡力了)


咱們能夠發現,若是在圓外存在交點,那麼在園內一定存在交點。

因此,式子和上面如出一轍。


知道了這個邊是怎麼回事。題目就轉換爲了:對於任意兩組邊,若是存在相交的關係,那麼他們不可以同時存在於一側(只能一內一外)。


那麼它就轉換爲了一個2-sat問題,每條邊要麼在內,要麼在外。
若是2-sat問題不熟能夠作一下【POI2001和平委員會】理解一下
(http://blog.csdn.net/qq_30974369/article/details/73927421)


接下來不難了,由於只須要判斷可行性,因此這類2-sat就很是容易解決,連完了邊,用Tarjan縮點(由於在一個強連通份量中的點要麼都選,要麼都不選),依次判斷便可。(具體實現看看代碼)

補充一下通常2-sat問題的作法:
1.根據題目含義建邊
2.使用Tarjan進行縮點
3.檢查每一組是否存在矛盾(某個點的選擇A和選擇B在同一個相連通份量裏面)
4.對縮出來的點進行拓撲排序,依次染色便可(必定存在解)
若是不可以理解爲何存在解,就到打開上面的連接,那個Blog裏面貼了一個大佬的2-sat詳解



這道題目作完了能夠接着作下一道【POJ3683】
http://blog.csdn.net/qq_30974369/article/details/74025251



/*

POJ 3207 Ikki's Story IV - Panda's Trick 

題目大意:
n個點連成一圈,指定m對點之間要有連線
能夠連在圈外也能夠連在圈內
問可否使全部的連線不相交

思路:
這題的點只是一個媒介
對於每一條邊而言,有兩種選擇:在圈內/在圈外
所以是一道比較顯然的2-sat問題
易證實:若兩條邊在圈內相交則一定在圈外相交
那麼,只要經過點的位置來考慮邊是否相交
用2-sat便可求解 

*/

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAX=100000;
struct Node
{
      int v,next;
}e[MAX];
int h[MAX],cnt=0;
int n,m,dfn[MAX],low[MAX],tim=0;
int Linkx[MAX],Linky[MAX];
int S[MAX],top=0;
int G[MAX],group=0;
bool vis[MAX];
inline void Add(int u,int v)
{
       e[cnt]=(Node){v,h[u]};
       h[u]=cnt++;
}
inline void Link();//連邊
inline void Tarjan(int u);//縮點 
int main()
{
       memset(h,-1,sizeof(h));
       cin>>n>>m;
       for(int i=1;i<=m;++i)
       {
             cin>>Linkx[i]>>Linky[i];//儲存每條邊的兩個端點 
             Linkx[i]++;Linky[i]++;//由於是從0開始的,都加1 
             if(Linkx[i]>Linky[i])
                swap(Linkx[i],Linky[i]);//x中存編號較小的點 
       }
       Link(); //連邊 
       for(int i=1;i<=2*m;++i)//縮點 
            if(!dfn[i])Tarjan(i);
       for(int i=1;i<=m;i++)
       {
            if(G[i]==G[i+m])
            {
                   cout<<"the evil panda is lying again"<<endl;
                   return 0;
            }
       }
       cout<<"panda is telling the truth..."<<endl;
       return 0;
}
void Link()//經過點的關係把全部的邊的關係找出,轉化爲2-sat 
{
       for(int i=1;i<=m;++i)
        for(int j=i+1;j<=m;++j)
        {
                 if(((Linkx[i]<=Linkx[j])&&(Linkx[j]<=Linky[i])&&(Linky[i]<=Linky[j]))
                  ||((Linkx[i]>=Linkx[j])&&(Linkx[i]<=Linky[j])&&(Linky[j]<=Linky[i])))
                  {
                        //出現交叉狀況不可以同時選擇 
                        Add(i,j+m);//i內j外 
                        Add(j,i+m);//反向邊 
                        Add(i+m,j);//i外j內
                        Add(j+m,i);//反向邊                
                        // i     表示第i條邊連在內側
                        // i+m   表示第i條邊連在外側 
                  }
        }
}
void Tarjan(int u)//縮點 
{
       dfn[u]=low[u]=++tim;
       S[++top]=u;
       vis[u]=true;
       int v;
       for(int i=h[u];i!=-1;i=e[i].next)
       {
             v=e[i].v;
             if(!dfn[v])
             {
                    Tarjan(v);
                    low[u]=min(low[u],low[v]);
             }
             else
               if(vis[v])
                low[u]=min(low[u],dfn[v]);
       }
       if(dfn[u]==low[u])
       {
              ++group;
              do
              {
                   v=S[top--];
                   vis[v]=false;
                   G[v]=group;
              }while(v!=u&&top);
       }
}
/*
寫在AC後面:
這題我WA了好久,並不知道本身哪裏錯了
只到最後,突然,發現,本身,
在,連邊的,時候,盡然,沒有,加{},把Add()打包。
我,真是,智障,了。。。 
*/
相關文章
相關標籤/搜索