zoukankan      html  css  js  c++  java
  • POJ 3207 Ikki's Story IV

    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;
    }
    
    自己选择的路,跪着也要走完。朋友们,虽然这个世界日益浮躁起来,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。
  • 相关阅读:
    类型:NodeJs;问题:默认IE的编码为utf8;结果:IE不能自动选择UTF-8编码解决办法
    类型:Java;问题:eclipse配置maven;结果:eclipse配置maven
    类型:Oracle;问题:oracle 时间加减;结果:ORACLE 日期加减操作
    类型:Ajax;问题:ajax调用ashx参数获取不到;结果:ashx文件获取$.ajax()方法发送的数据
    类型:JQuery;问题:ajax调用ashx文件;结果:ashx文件怎么获取$.ajax()方法发送的json数据
    类型:Jquery;问题:jquery调用后台带参数方法;结果:利用JQuery的$.ajax()可以很方便的调用asp.net的后台方法。
    问题:asp.net 点击button按钮调到页面顶部;结果:asp.net点击一个按钮,使页面跳转到本面页上的指定位置
    问题:ClientIDMode属性;结果:ASP.NET 4.0的ClientIDMode属性
    金木水火土五行查询表
    并发编程的基础
  • 原文地址:https://www.cnblogs.com/SYCstudio/p/7134056.html
Copyright © 2011-2022 走看看