zoukankan      html  css  js  c++  java
  • 【POJ3207】Ikki's Story IV

    测试地址:Ikki’s Story IV - Panda’s Trick
    题目大意:n个点排成一个环形,现在要连m条边,一条边要么连在环形的里面,要么连在环形的外面,问有没有一种连法使得这些边两两之间不相交(在点上相交不算)?
    做法:我的博客已经持续更新一周年了,可喜可贺可喜可贺……
    至于为什么这一个月没有更新呢……说来话长,你们只需要知道我NOI2017考崩了就好(T_T)。
    这一题是一个2-SAT的模型。
    什么叫2-SAT?2-SAT是2-适定性问题的简称,差不多是这样一个模型:给若干个东西,这些东西两两之间可能有限制条件,问有没有满足所有条件的选法。之所以命名为2-SAT就是因为这些限制条件是两两之间的条件,而k-SAT问题在k>2时已经被证明是NPC问题,只有2-SAT有比较优美的解法。
    至于解2-SAT,网上有很多教程,这里就不细讲了,大概就是将每个东西分成两点,分别表示取或不取这个东西(以下称这两个点为A和非A),那么问题就转化为在每组两个点里取且仅取一个点,问有没有满足限制条件的取法。我们可以把两两之间的限制条件变成有向边的形式,通常有向边A->B表示要取A必须也取B,经过一些转化后就可以求解,主要是用强连通分量,这里就不再赘述了。
    那么这一题的模型要转化为2-SAT十分简单,每条边有连里边和连外边两种选择,且只能选择一种,这已经提示了这题是2-SAT。再看限制条件,两条边不能在点外相交。经过探究,两条边在点外相交的充要条件是:两条边没有公共顶点并连在同一侧,且其中一条边的两个端点分别处在另一条边将环分成的两个部分。于是我们判断两条边是不是满足了“没有公共顶点”和最后那个条件(太长懒得写),如果同时满足,那么就说明它们的连法受到限制:不能连在环形的同侧,也就是说,对于冲突的两条边A,B,我们不能同时选择点A和B,也不能同时选点非A和非B。那么应该怎么连边呢?很简单,点A,B不能同时被选,那么就表示选了A就必须选非B,选了B就必须选非A,按照这样的转化连边即可。建完图之后,求图的强连通分量,如果在同一组的两个点处在同一个强连通分量,那么问题无解,否则问题肯定有解。其实可以求出任意一个解,但这题只需要判断有没有解,所以就下次再说啦~
    总的时间复杂度:O(m2),可以通过本题(0ms)。
    以下是本人代码:

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    int n,m,a[510],b[510],tot=0,tim=0,tott,first[2010]={0};
    struct {int v,next;} e[2000010];
    int dfn[1010],low[1010],belong[1010]={0},stack[1010]={0},top=0;
    int in[2010]={0};
    bool vis[1010]={0};
    
    void insert(int a,int b)
    {
        e[++tot].v=b;
        e[tot].next=first[a];
        first[a]=tot;
    }
    
    void dfs(int v)
    {
        vis[v]=1;
        dfn[v]=++tim;
        low[v]=dfn[v];
        stack[++top]=v;
        int now=top;
        for(int i=first[v];i;i=e[i].next)
        {
            if (!vis[e[i].v])
            {
                dfs(e[i].v);
                low[v]=min(low[v],low[e[i].v]);
            }
            else if (!belong[e[i].v]) low[v]=min(low[v],dfn[e[i].v]);
        }
        if (low[v]==dfn[v])
        {
            tott++;
            for(int i=now;i<=top;i++)
                belong[stack[i]]=tott;
            top=now-1;
        }
    }
    
    void tarjan()
    {
        for(int i=0;i<2*m;i++)
            if (!vis[i]) dfs(i);
        tott++;
        for(int i=1;i<=top;i++)
            belong[stack[i]]=tott;
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&a[i],&b[i]);
            if (a[i]>b[i]) swap(a[i],b[i]);
            for(int j=0;j<i;j++)
            {
                if (a[i]==a[j]||b[i]==a[j]||a[i]==b[j]||b[i]==b[j]) continue;
                bool f1=(a[j]>a[i]&&a[j]<b[i]),f2=(b[j]>a[i]&&b[j]<b[i]);
                if (f1!=f2)
                {
                    insert(2*j,2*i+1);
                    insert(2*j+1,2*i);
                    insert(2*i,2*j+1);
                    insert(2*i+1,2*j);
                }
            }
        }
    
        tott=2*m-1;
        tarjan();
    
        for(int i=0;i<m;i++)
            if (belong[2*i]==belong[2*i+1])
            {
                printf("the evil panda is lying again");
                return 0;
            }
        printf("panda is telling the truth...");
    
        return 0;
    }
    
  • 相关阅读:
    mysql_fetch_array()和mysql_fetch_assoc()两个函数的区别
    在vmware workstation7上安装centos5.5的一些问题
    如何在CentOS下安装VMwareTools工具
    ResultSetMetaData类的介绍
    火狐快捷键
    ResultSetMetaData类的介绍
    jstl标签的使用
    e.printStackTrace()介绍
    mysql初学指南
    在SQL server中,怎么区别char跟varchar?
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793620.html
Copyright © 2011-2022 走看看