LA4452 dfs及其应用(2-SAT)

题意:每个人最多对4个法案提意见,问是否存在一种方案,使得每个人的意见通过一半以上。若可以通过一半以上,输出每个方案可能的结果(一定通过/一定不通过/两个结果都有可能)

思路:意见为1/2时,全部通过,意见为3/4时,至多否决一个意见,从而转化为“意见”型2-sat问题


“意见”型变量
1.先把意见用变量表示:把每个人的意见表示成数x的形式(x是某个布尔变量成立(x=2*y+1)/不成立(x=2*y)),则该意见成立则为x,不成立则为x^1
2.再根据意见间的逻辑关系,表示成语句。
意见型语句:
1.k个意见中至多有一个不成立 add(xi^1,xj)
2.k个意见中至多有一个成立 add(xi,xj^1)
3.k个意见全部成立 add(xi^1,xi)
4.k个意见全部不成立 add(xi,xi^1)


2-SAT算法对dfs的理解:dfs(u):把某个布尔变量赋值为u后,整个图是否引起矛盾。
通过dfs可以判断是否可以让某个布尔变量取特定的值


<span style="font-size:14px;">#include<stdio.h>
#include<string.h>
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
#define maxn 5010
#define INF 1<<28
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;


int n,m,adv[maxn],S[maxn<<2],c;
vector<int> g[maxn];
bool mark[maxn<<2];

void add(int u,int v) { g[u].push_back(v); }

void build(int k){
    if(k==1) add(adv[1]^1,adv[1]);
    if(k==2) add(adv[1]^1,adv[1]),add(adv[2]^1,adv[2]);
    if(k==3||k==4) for(int i=1;i<=k;i++) for(int j=1;j<=k;j++) if(i!=j) add(adv[i]^1,adv[j]);
}

bool dfs(int u){//u成立:判断该图是否产生矛盾
    if(mark[u^1]) return false;
    if(mark[u]) return true;
    mark[u]=1,S[c++]=u;
    for(int i=0;i<g[u].size();i++) if(!dfs(g[u][i])) return false;
    return true;
}

bool judge_2sat(){
    for(int i=0;i<2*n;i+=2){
        if(mark[i]&&mark[i^1]) return false;
        else if(!mark[i]&&!mark[i^1]){
            c=0;
            if(!dfs(i)){
                while(c) mark[S[--c]]=0;//还原
                if(!dfs(i^1)) return false;
            }
        }
    }
    return true;
}

<strong>bool ok(int u){
    mem(mark,0);
    if(!dfs(u)) return false;
    return true;
}
</strong>
int main(){
    int T=0;
    //freopen("a.txt","r",stdin);
    while(scanf("%d%d",&n,&m)!=EOF){
        if(!n&&!m) break;
        for(int i=0;i<2*n;i++) g[i].clear();//点的数量写成了n!
        for(int i=1;i<=m;i++){
            int k,a;char b[3]; scanf("%d",&k);
            for(int j=1;j<=k;j++){
                scanf("%d%s",&a,b); a-=1;//id变化范围0-(n-1)
                if(b[0]=='y') adv[j]=2*a+1; else adv[j]=2*a;
            }
            build(k);//k个意见至少成立一半
        }
        printf("Case %d: ",++T);
        mem(mark,0);
        if(!judge_2sat()) { printf("impossible\n"); continue; }
        for(int i=0;i<n;i++){
            bool f1=ok(2*i),f2=ok(2*i+1);
            if(f1&&f2) printf("?");
            else if(f1&&!f2) printf("n");
            else if(!f1&&f2) printf("y");
        }
        printf("\n");
    }
    return 0;
}</span>



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值