• HDU3062 2-sat


      1 /*HDU3062
      2 2-sat入门
      3 虽然是入门,关于2-sat算法的实质的理解还是花了蛮久的时间。
      4 这道题还是有一些思维上的技巧。因为每对夫妇,最多且必须要去一个人,就是总共去了n个人。
      5 我开始时以每个人分别去否建模,这样还要满足上面的条件,显然算法是难以实现的。
      6 后来发现,只要以一对夫妻为对象,2i是丈夫去,2i+1是妻子去,非2i即2i+1,就像一件事情,非true即false
      7 所以,不仅是一个对象有true,false两种状态,推广开来,只要是二选一(排他率)的二值问题,就可以向2-sat方向思考。
      8 连边:因为2i和2i+1的相反关系,程序中已经能判断,所以不需考虑边的关系。
      9 仇人关系,先转化成对应的点a,b。(非a且b)或(a和非b),即是a^1和b连边,a和b^1连边
     10 判断是否是这个2-sat有解就可以了
     11 */
     12 #include <stdio.h>
     13 #include <stdlib.h>
     14 #include <string.h>
     15 #include <math.h>
     16 #include <ctype.h>
     17 #include <string>
     18 #include <iostream>
     19 #include <sstream>
     20 #include <vector>
     21 #include <queue>
     22 #include <stack>
     23 #include <map>
     24 #include <list>
     25 #include <set>
     26 #include <algorithm>
     27 #define INF 0x3f3f3f3f
     28 #define LL long long
     29 #define eps 1e-7
     30 #define maxn 1100
     31 using namespace std;
     32 struct TwoSAT{
     33     int n;
     34     vector<int> G[maxn*2];//注意点集的大小
     35     bool mark[maxn*2];//联系《2-sat算法解析》中的红蓝标色,夫妻不能被标同一种颜色;因为仇人间没有连边,所以在图本身,不可能将两人标同一个颜色
     36     int S[maxn*2],c;//存储当前被标记的点,可用于标记的回退
     37 
     38     bool dfs(int x)
     39     {
     40         if (mark[x^1]) return false;//真假同时被标记,逻辑矛盾
     41         if (mark[x]) return true;//x被标记,意味着下面的节点也被标记,思想是记忆化搜索
     42         mark[x]=true;
     43         S[c++]=x;
     44         for(int i=0;i<G[x].size();i++)
     45             if(!dfs(G[x][i])) return false;//同一个强联通分量应该表上同一种颜色
     46         return true;
     47     }
     48 
     49     void init(int n)
     50     {
     51         this->n=n;
     52         for(int i=0;i<n*2;i++) G[i].clear();
     53         memset(mark,0,sizeof(mark));
     54     }
     55 
     56     void add_edge(int u,int v)//这个地方灵活多变一点
     57     {
     58         G[u].push_back(v);
     59 //        cout<<u<<"->"<<v<<endl;
     60     }
     61 
     62     bool solve()
     63     {
     64         for(int i=0;i<n*2;i+=2)
     65         {
     66             if(!mark[i] && !mark[i^1])//真假都没被标记才需dfs,思考一下,原书上写的是[mark+1],这是由i的取值和步长决定的,这里更改,使逻辑含义统一
     67             {
     68                 c=0;//记得清零
     69                 if(!dfs(i))//将i标记为true
     70                 {
     71                     while(c>0) mark[S[--c]]=false;
     72                     if (!dfs(i^1)) return false;//更改初始标号颜色。只要有一个对象不能“二选一”,则2-sat无解
     73                 }
     74             }
     75         }
     76         return true;
     77     }
     78 }sat;
     79 //2i是丈夫去,2i+1是妻子去,
     80 //仇人关系,先转化成对应的点a,b。(非a且b)或(a和非b),即是a^1和b连边,a和b^1连边,注意是双向边,满足对偶性
     81 int n,m;
     82 int nextint()
     83 {
     84     int x;
     85     scanf("%d",&x);
     86     return x;
     87 }
     88 int main()
     89 {
     90     while(cin>>n>>m)
     91     {
     92 //        cout<<"Sdf"<<endl;
     93         sat.init(n);//记住标号为相应的0--n-1
     94         for(int i=1;i<=m;i++)
     95         {
     96             int a1,a2,c1,c2;
     97             int a,b;
     98             a1=nextint();
     99             a2=nextint();
    100             c1=nextint();
    101             c2=nextint();
    102             if(c1==0) a=a1*2+1;else a=a1*2;//0是妻子
    103             if(c2==0) b=a2*2+1;else b=a2*2;
    104 //            sat.add_edge(a^1,b);
    105             sat.add_edge(b,a^1);
    106             sat.add_edge(a,b^1);
    107 //            sat.add_edge(b^1,a);
    108         }
    109         if (sat.solve()) cout<<"YES"<<endl;else cout<<"NO"<<endl;
    110     }
    111 }    
  • 相关阅读:
    8 Range 对象
    7 Worksheet 对象
    6 Workbook 对象
    5 Application 对象
    Windows路径
    windows 下操作目录(使用DOS命令)
    Windows 批处理
    6 WPF控件
    Lexer and parser generators (ocamllex, ocamlyacc)
    4.9 Parser Generators
  • 原文地址:https://www.cnblogs.com/little-w/p/3577629.html
Copyright © 2020-2023  润新知