2-sat

2-SAT算法本身并不难,关键是连边,不过只需要充分理解好边的概念:a->b即选a必选b。

a、b不能同时选:选了a就要选b',选了b就要选a'。

a、b必须同时选:选了a就要选b,选了b就要选a,选了a'就要选b',选了b'就要选a'。

a、b必须选一个:选了a就要选b',选了b就要选a',选了a'就要选b,选了b'就要选a。

a、b至少选一个:选了a' 就要选b,选了b' 就要选a

※a必须选:a'->a。

https://ac.nowcoder.com/acm/contest/327/F
题目描述:处女座进行了一次探险,发现了一批宝藏。如果他获得这批宝藏,那么他一辈子都不需要工作了。但是处女座遇到了一个难题。

宝藏被装在n个宝箱里,宝箱编号为1,2,…,n,只有所有宝箱在某一时间被打开,处女座才能获得宝藏。有m个开关,每个开关控制k个宝箱,如果按下一个开关,那么这k个宝箱的开关状态都会发生改变(从开启变成关闭,从关闭变成开启),处女座想知道他能否获得这批宝藏

题解:2-sat

  1 #include<iostream>
  2 #include<string>
  3 #include<algorithm>
  4 #include<cstdio>
  5 #include<vector>
  6 #include<cstring>
  7 using namespace std;
  8 typedef long long ll;
  9 const int maxn=4e5+5;
 10 vector<int>g[maxn];
 11 int a[maxn],dfn[maxn<<1],low[maxn<<1],col[maxn<<1],head[maxn<<1],stk[maxn<<1],tim,tnt,cnt,col0;
 12 bool instk[maxn<<1];
 13 struct edge{
 14     int y;
 15     int x;
 16     int nex;
 17 }e[maxn<<1];
 18 void adde(int q,int w){
 19     e[cnt].x=q;
 20     e[cnt].y=w;
 21     e[cnt].nex=head[q];
 22     head[q]=cnt++;
 23 }
 24 void tarjan(int u){
 25     dfn[u]=low[u]=++tim;
 26     instk[u]=1;
 27     stk[++tnt]=u;
 28     for(int i=head[u];i!=-1;i=e[i].nex){
 29         int v=e[i].y;
 30         if(!dfn[v]){
 31             tarjan(v);
 32             low[u]=min(low[u],low[v]);
 33         }
 34         else if(instk[v]){
 35             low[u]=min(low[u],dfn[v]);
 36         }
 37     }
 38     if(dfn[u]==low[u]){
 39         col0++;
 40         int x;
 41         do{
 42             x=stk[tnt];
 43             col[x]=col0;
 44             instk[x]=0;
 45             tnt--;
 46         }while(x!=u);
 47     }
 48 }
 49 int main(){
 50     int n,m;
 51     scanf("%d%d",&n,&m);
 52     memset(head,-1,sizeof(head));
 53     for(int i=1;i<=n;i++)scanf("%d",&a[i]);
 54     for(int i=1;i<=m;i++){
 55         int k;
 56         scanf("%d",&k);
 57         for(int j=0;j<k;j++){
 58             int x;
 59             scanf("%d",&x);
 60             g[x].push_back(i);
 61         }
 62     }
 63     int f=0;
 64     for(int i=1;i<=n;i++){
 65        if(g[i].size()==0){
 66             if(a[i]==1)
 67              {f=1;break;}
 68        }
 69        if(g[i].size()==1){
 70             if(a[i]){
 71                 adde(g[i][0]+m,g[i][0]);
 72             }
 73             else{
 74                 adde(g[i][0],g[i][0]+m);
 75             }
 76        }
 77        if(g[i].size()==2){
 78             if(a[i]){
 79                 adde(g[i][0],g[i][1]+m);
 80                 adde(g[i][1],g[i][0]+m);               
 81                 adde(g[i][1]+m,g[i][0]);
 82                 adde(g[i][0]+m,g[i][1]);
 83             }
 84             else{
 85                 adde(g[i][0],g[i][1]);
 86                adde(g[i][1],g[i][0]);               
 87                 adde(g[i][1]+m,g[i][0]+m);
 88                 adde(g[i][0]+m,g[i][1]+m);              
 89             }
 90        }
 91     }
 92     for(int i=1;i<=m*2;i++){
 93         if(!dfn[i]){
 94             tarjan(i);
 95         }
 96     }
 97     for(int i=1;i<=m;i++){
 98         if(col[i]==col[i+m]){
 99             f=1;
100             break;
101         }
102     }
103     if(!f)printf("YES\n");
104     else printf("NO\n");
105     return 0;
106 }
View Code

 

转载于:https://www.cnblogs.com/MekakuCityActor/p/10556378.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值