zoukankan      html  css  js  c++  java
  • 【POI2001】【HDU1814】和平委员会

    题面

    Description

    根据宪法,Byteland民主共和国的公众和平委员会应该在国会中通过立法程序来创立。 不幸的是,由于某些党派代表之间的不和睦而使得这件事存在障碍。
    此委员会必须满足下列条件:
    每个党派都在委员会中恰有1个代表,
    如果2个代表彼此厌恶,则他们不能都属于委员会。
    每个党在议会中有2个代表。代表从1编号到2n。 编号为2i-1和2i的代表属于第I个党派。
    任务是写一程序:
    输入党派的数量和关系不友好的代表对,
    计算决定建立和平委员会是否可能,若行,则列出委员会的成员表。

    Input

    第一个行有2非负整数n和m。 他们各自表示:党派的数量n,1<=n<=8000和不友好的代表对m,0 <=m <=20000。 在下面m行的每行为一对整数a,b,1<=a

    Output

    如果委员会不能创立,输出中应该包括单词NIE。若能够成立,输出中应该包括n个从区间1到2n选出的整数,按升序写出,每行一个,这些数字为委员会中代表的编号。 如果委员会能以多种方法形成,程序输出字典序最小的一个。

    Sample Input

    3 2
    1 3
    2 4

    Sample Output

    1
    4
    5


    (题外话)
    原来一直觉得做了一道题,做了就是做了,就不用管了。现在觉得,每做完一道题,不管怎么样,还是写一写自己的总结。不管给谁看吧,有个记录自己也方便复习。同样的,以后自己回首再看自己的OI历程,才会觉得自己还是有所收获的。

    解决问题

    不管了,言归正传。这是一道2-sat的(模板)题。
    关于2-sat问题是什么,我只是小蒟蒻,并不是我能够说清的问题(只能够自己理解,但是我并不一定能够说得清楚)。
    先贴一个dalao的blog供大家学习2-sat,这个讲的非常清楚
    *http://blog.csdn.net/jarjingx/article/details/8521690 *

    这是一道简单的题目,2-sat最暴力的解法。
    (因为题目是从简单到难,方法也就从简单到复杂,其它的更加复杂的内容就写在别的地方)


    先化简一下题意:就是有n党派,每个党派两个人,只能够从中选择一个人进入委员会。同时,有m组关系,表示某两个人不能够同时出现在委员会中。求出字典序最小的委员会名单。


    这就是一个2-sat问题,因为要求出最小字典序,只能够用最暴力的方法,时间复杂度为O(nm)。




    先看一看矛盾是什么意思。不妨设i的同党派的人的编号是i'。那么某两个人(a,b)矛盾,即意味着选择了a就只能选择b',选择了b就只能选择a',即(a,b')(b,a'),那么这两种就是可行性方案。

    那么,对于每一组矛盾(a,b),就进行连边(a,b')(b,a')




    拿样例举例就是这样:
    样例联完点之后就是这样

    因为1,3矛盾、2,4矛盾,其实连出来的的线重合了,就只有这两条




    先贴一段代码,可以自己大致估摸一下什么意思

    //work()函数
    //Oth(x)指的就是x'
    bool Work()
    {
    	  memset(Col,0,sizeof(Col));
    	  for(int i=1;i<=n*2;++i)
    	  {
    	  	    if(Col[i])
    			   continue;
    			cnt=0;
    			if(!Paint(i))
    			{
    				 for(int j=1;j<=cnt;++j)
    				 	  Col[Ans[j]]=Col[Oth(Ans[j])]=0;
    				 if(!Paint(Oth(i)))
    				   return false; 
    			}
    			else 
    			 continue; 
          }
          return true;
    }
    

    ```cpp //Paint()函数 bool Paint(int x)//染色法 { if(Col[x]!=0) return Col[x]%2; Col[x]=1;Col[Oth(x)]=2; Ans[++cnt]=x; for(int i=0;i如果代码没有读懂就接着向下看


    分析一下过程
    1.读入数据,并进行连边操作(我存边偷懒用的vector)
    2.顺序搜索,依次进行染色的尝试(DFS过程),染为可行和不可行
    3.检查可行性,输出结果




    第1步已经分析过了(上面呢),看第2步。

    这里用到的方法是染色法。因为要求字典序最小,每次从前向后循环,每找到一个未染色的点就对其进行DFS的染色尝试(优先尝试染为可行,这要保证字典序最小)。
    那么接下来大致过程如下:
    1.首先对当前点x进行染色,染为可行,其党派内的对应结点x'则染为不可行。
    2.访问所有和x相连的结点vi,依次进行搜索。
    3.如果vi未被染过颜色,同x的染色一样继续进行。如果vi已经被染过色,那么检查它是否可行。如果产生了矛盾,则证明当前染色是错误的,需退回将x'染为可行,x不可行(即重新对x'进行搜索,work()函数中的Paint(Oth(x))语句的含义 )。
    4.如果重新尝试染Paint(x')也不行,则意味着无解,退出函数并输出,否则继续依次尝试染色。
    5.如果所有结点都被染上了颜色,程序结束,访问所有结点,输出所有被染为可行的结点


    ~~接下来找一道难一点的2-sat问题~~ http://blog.csdn.net/qq_30974369/article/details/73930235
    ~~再做完了继续来一道更难的~~ http://blog.csdn.net/qq_30974369/article/details/74025251
    *** 如果还没懂就好好读读完整的程序(带了注释) ~~顺带再提一句,希望能够原谅我苦逼的格式(并不太会调)~~
    ```cpp /* POI2001 和平委员会 by yyb */ #include #include #include #include #include #include #include using namespace std;

    define Oth(x) (x%2==0?x-1:x+1)

    const int MAX=8005;

    vector e[MAX2];
    short int Col[MAX
    2];
    int Ans[MAX];
    int n,m,cnt,u,v;

    int read()//读入优化
    {
    int x=0;
    short int t=1;
    char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-'){t=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x10+ch-48;ch=getchar();}
    return x
    t;
    }

    bool Paint(int x)//染色法
    {
    if(Col[x]!=0)//已经染过颜色了
    return Col[x]%2;//若x染色为1则不矛盾,若x染色为2,则与原来染色矛盾
    Col[x]=1;Col[Oth(x)]=2;//当前x染颜色1,另一个染为颜色2
    Ans[++cnt]=x;//记录一组染色
    for(int i=0;i<e[x].size();++i)
    {
    if(!Paint(e[x][i]))//向所有联通的点搜索
    return false;//如果染色出现了矛盾,则返回
    }
    return true;//当前x染色成功
    }

    bool Work()
    {
    memset(Col,0,sizeof(Col));//清空染色的数组
    for(int i=1;i<=n*2;++i)//枚举所有的人
    {
    if(Col[i])//已经染过色
    continue;//不用搜索
    cnt=0;//当前染色的组数
    if(!Paint(i))//将i染色失败
    {
    for(int j=1;j<=cnt;++j)//本次染的颜色全部清空
    Col[Ans[j]]=Col[Oth(Ans[j])]=0;
    if(!Paint(Oth(i)))//将i的另一个对应点染色
    return false;//如果也失败则无解
    }
    else //染色成功
    continue;//继续染色
    }
    return true;
    }

    int main()
    {
    n=read();m=read();
    for(int i=1;i<=m;++i)//读入并建立边
    {
    u=read();
    v=read();
    e[u].push_back(Oth(v));
    e[v].push_back(Oth(u));
    }
    if(Work())//尝试染色成功
    {
    for(int i=1;i<=n*2;++i)
    if(Col[i]==1)
    cout<<i<<endl;
    }
    else//染色失败,无解
    cout<<"NIE"<<endl;
    return 0;
    }

  • 相关阅读:
    pionter指针小结
    C++笔记 5
    C++笔记 3
    ipad safari 滚动(overflow)解决方案
    IE9 BUG overflow :auto 底部空白解决方案
    asp.net 导出EXCEL超高兼容(不用装Excel)
    jquery post 同步异步总结
    jquery-alert对话框
    左固定右边自适应框架
    删除Cookies
  • 原文地址:https://www.cnblogs.com/cjyyb/p/7197133.html
Copyright © 2011-2022 走看看