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

POJ 3207 Ikki’s Story IV - Panda’s Trick(2-sat問題)

Description

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.

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…

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

Input

The input contains exactly one test case.

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.

Output

Output a line, either “panda is telling the truth…” or “the evil panda is lying again”.

Sample Input

4 2
0 1
3 2

Sample Output

panda is telling the truth…

Http

POJ:https://vjudge.net/problem/POJ-3207

Source

2-sat

題目大意

給定一個圓及其上面的n個點,現在要連接上面的m對點,可以從圓外或圓內連接,要求不能相交。現在問這樣的方案是否存在

解決思路

對於圓內和圓外我們可以把其看做兩種狀態,那麼對於每一對點,我們把從圓外連接記作i,把從圓內連接記作i+m。

那如何連接邊呢?我們知道在2-sat問題中若連接i->j則表示若取i則必取j,轉換到這一題就是要找出那些會出現矛盾的點對,即兩組點對不能同時從外面或同時從裏面連接。

爲了方便操作,我們在輸入的時候就點對(x,y)x,y中較小的一個放到x中,大的放到y中,這樣我們就可以把圓化成一個線段,在線段上處理了(爲什麼呢,仔細想一想)。

掃描所有點對,每次枚舉點對i,j,那麼出現矛盾會是什麼情況呢?

矛盾情況1

當xj在xi與yi之間且yj不再xi與yi之間時,兩組點對不能同時在圓外或圓內。所以可以以此建圖。

因爲這個題目只要判斷可行性,所以在判斷2-sat時既可以用在這一題中的dfs染色判斷法,也可以用Tarjan縮點求強聯通分量的方法。因爲上一篇文章已經介紹過了染色法,加上這一題數據範圍更大,所以這裏介紹一下Tarjan法。

關於如何用Tarjan求強連通分量,這裏不再重複(如果不知道,可以到這篇文章去看一看(施工中)),那麼主要講一講爲什麼Tarjan縮點後就可以判斷2-sat的可行性。

因爲我們在2-sat建圖時對於一條邊i->j的意思是選i必須選j,而縮點後每一個強連通分量代表着互相可達,即表示若選擇其中的一個則整個強聯通分量中的點都必須選擇。而若此時i與i+m在同一個強連通分量裏,說明無解(一對點總不能既從圓外連,又從圓內連吧)。

代碼

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<vector>
#include<stack>
using namespace std;

const int maxN=2001;
const int inf=2147483647;

int n,m;
int Readx[maxN];
int Ready[maxN];
vector<int> E[maxN];//i與i+m爲對應點
//Tarjan
int cnt=0;
int dfn[maxN];
int low[maxN];
bool instack[maxN];
stack<int> S;
//連通塊(這裏我就用拼音寫法啦)
int LTKcnt=0;
int LTK[maxN];

void Read_and_Init();//讀入並建圖
void Link(int x,int y);//連接x與y
void tarjan(int u);
bool check();

int main()
{
    Read_and_Init();
    memset(instack,0,sizeof(instack));
    memset(dfn,0,sizeof(dfn));
    while (!S.empty())
        S.pop();
    for (int i=1;i<=2*m;i++)
        if (dfn[i]==0)
            tarjan(i);
    if (check())
        cout<<"panda is telling the truth..."<<endl;
    else
        cout<<"the evil panda is lying again"<<endl;
}

void Read_and_Init()
{
    cin>>n>>m;
    int a,b;
    for (int i=1;i<=m;i++)
    {
        cin>>Readx[i]>>Ready[i];
        Readx[i]++;
        Ready[i]++;
        if (Readx[i]>Ready[i])//爲了方便後面判斷,將小的點放在前面
            swap(Readx[i],Ready[i]);
    }
    for (int i=1;i<=m;i++)
        for (int j=i+1;j<=m;j++)
        {
            if ( ((Readx[i]<=Readx[j])&&(Readx[j]<=Ready[i])&&(Ready[i]<=Ready[j]))
                 || ((Readx[i]>=Readx[j])&&(Readx[i]<=Ready[j])&&(Ready[j]<=Ready[i]))  )//這裏即表示若i與j矛盾
            {
                Link(i,j+m);
                Link(j,i+m);
                Link(i+m,j);
                Link(j+m,i);
            }
        }
    return;
}

void Link(int x,int y)
{
    E[x].push_back(y);
    return;
}

void tarjan(int u)//Tarjan算法,不多說
{
    cnt++;
    dfn[u]=low[u]=cnt;
    instack[u]=1;
    S.push(u);
    for (int i=0;i<E[u].size();i++)
    {
        int v=E[u][i];
        if (dfn[v]==0)
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else
            if (instack[v]==1)
                low[u]=min(low[u],dfn[v]);
    }
    if (dfn[u]==low[u])
    {
        int v;
        LTKcnt++;
        do
        {
            v=S.top();
            S.pop();
            instack[v]=0;
            LTK[v]=LTKcnt;
        }
        while (u!=v);
    }
    return;
}

bool check()//檢查是否有i與i+m在同一連通塊
{
    for (int i=1;i<=m;i++)
        if (LTK[i]==LTK[i+m])
            return 0;
    return 1;
}
發佈了33 篇原創文章 · 獲贊 4 · 訪問量 8284
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章