UVA 11294 Wedding(2-sat)

2-sat。不错的一道题,学到了不少。

需要注意这么几点:

1、题目中描述的是有n对夫妇,其中(n-1)对是来为余下的一对办婚礼的,所以新娘只有一位。

2、2-sat问题是根据必然性建边,比如说A与B二选一,那么当不选A时,必然选B。在本题中,我们所能确定的必然性只有一种:当一对通奸者中的一个人出现在新娘的对面时,另一个必须在新娘的同侧。一开始,我每次建的是两条边,即由新娘指向对面的一人,再从这个人,指向与新娘同侧的另一人(语言描述较困难,但我尽量简明的表达出来)。这是一种假设,因为新娘既可能在左边,又可能在右边。但是这不是必然性:“新娘指向对面的一个人”,这条边不存在必然关系。

    解决办法:我们假定新娘就在某一侧。那么是否会影响最终结果呢?不会,因为不管在那一侧,只要有正确方案,转换一下方向,总归是成立的。

3、如何确定新娘就在某一侧?

    建边的过程,我们假定了新娘的位置,但是并没有确定的在程序中表现出,新娘就在这一侧。

    方法:(1)mark[0]=1;明确的表示出新娘已被标记,但需要注意的是,每次dfs标记的是一条链,或者说是以你选择的点为根的一棵树,所以,只是这样做是不够的,需要单独对mark[0]做一次dfs。

             (2)如代码中写的,dfs过程中,若是在 dfs(0) 时失败了,那么就return false;不给 dfs(1) 机会。

注意:准确的说,确定了新娘的位置,即确定了“0w”和“0h”的位置,那么所有包含“0w”或“0h”的关系,所建的边都只能有一条。不过删掉约束条件一样能ac,仔细想来,是solve()中if(i==0)return false;的功劳,因为我只允许dfs(0)成功,所以即使建了另一条边,也不会有机会搜的。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<cstdlib>
  4 #include<vector>
  5 #include<algorithm>
  6 using namespace std;
  7 
  8 const int MAXN=333;
  9 
 10 int n;
 11 bool mark[MAXN<<1];
 12 int S[MAXN<<1],c;
 13 vector<int >G[MAXN<<1];
 14 
 15 void init(int n)
 16 {
 17     for(int i=0;i<(n<<1);i++)
 18         G[i].clear();
 19     memset(mark,0,sizeof(mark));
 20 }
 21 
 22 void add(int x,int xval,int y,int yval)
 23 {
 24     x=(x<<1)+xval;
 25     y=(y<<1)+yval;
 26     G[x].push_back(y);
 27 }
 28 
 29 bool dfs(int x)
 30 {
 31     if(mark[x^1]){
 32         return false;
 33     }
 34     if(mark[x]){
 35         return true;
 36     }
 37     S[c++]=x;
 38     mark[x]=true;
 39     for(int i=0;i<G[x].size();i++)
 40     {
 41         if(!dfs(G[x][i])){
 42             return false;
 43         }
 44     }
 45     return true;
 46 }
 47 
 48 bool solve()
 49 {
 50     for(int i=0;i<(n<<1);i+=2)
 51     {
 52         if(!mark[i]&&!mark[i+1]){
 53             c=0;
 54             if(!dfs(i)){
 55                 if(i==0)      //如果新娘在 0 这一侧这一前提不成立,则 no solusion
 56                     return false;
 57                 while(c>0)
 58                     mark[S[--c]]=false;
 59                 if(!dfs(i+1))
 60                     return false;
 61             }
 62         }
 63     }
 64     return true;
 65 }
 66 
 67 int main()
 68 {
 69     int m,a,b;
 70     char x,y;
 71     while(~scanf("%d%d",&n,&m))
 72     {
 73         if(!n&&!m)
 74             return 0;
 75 
 76         init(n);
 77         for(int i=0;i<m;i++)           //固定新娘在 0 这一侧
 78         {
 79             scanf("%d%c %d%c",&a,&x,&b,&y);
 80             if(x=='h'&&y=='h'){
 81                 add(a,0,b,1);
 82                 add(b,0,a,1);
 83             }else if(x=='w'&&y=='w'){
 84                 add(a,1,b,0);
 85                 add(b,1,a,0);
 86             }else if(x=='h'&&y=='w'){
 87                 add(a,0,b,0);
 88                 add(b,1,a,1);
 89             }else if(x=='w'&&y=='h'){
 90                 add(a,1,b,1);
 91                 add(b,0,a,0);
 92             }
 93         }
 94         
 95         if(solve()){
 96             for(int i=1;i<n;i++)
 97             {
 98                 if(mark[i<<1])
 99                     printf("%dw",i);
100                 else
101                     printf("%dh",i);
102                 if(i!=n-1)
103                     printf(" ");
104             }
105             printf("\n");
106         }else
107             printf("bad luck\n");
108     }
109     return 0;
110 }
111 /*
112 附上一组数据,让我发现了第三个问题
113 10 10
114 6h 2w
115 1h 9w
116 1w 3w
117 9w 0h
118 1h 9h
119 4h 1w
120 7h 2w
121 1h 0h
122 0h 9w
123 0h 3h
124 */
View Code

 

转载于:https://www.cnblogs.com/zstu-abc/p/3244037.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值