hdu1814 Peaceful Commission(2 SAT【字典序最小解)

题目链接

题意:
著名的和平委员会选举问题
委员会必须满足以下条件:
1.每一方在委员会只有一名代表
2.如果两位代表相互讨厌,他们不能同时入选委员会

分析:
经典2 SAT问题

(A,A),(B,B) ( A , A ′ ) , ( B , B ′ )
如果 A,B A , B 之间存在矛盾: A>B,B>A A − > B ′ , B − > A ′

其实这道题的重点就在于构造字典序最小解
在蓝书上,lrj表示对于字典序最小解,一般是用暴力构造:

大致过程如下:

  • 首先对当前点x进行染色,染为可行,其党派内的对应结点x’则染为不可行
  • 访问所有和x相连的结点vi,依次进行搜索
  • 如果vi未被染过颜色,同x的染色一样继续进行
    如果vi已经被染过色,那么检查它是否可行
    如果产生了矛盾,则证明当前染色是错误的,需退回将x’染为可行,x不可行
  • 如果重新尝试染Paint(x’)也不行,则意味着无解,退出函数并输出,否则继续依次尝试染色
  • 如果所有结点都被染上了颜色,程序结束,访问所有结点,输出所有被染为可行的结点

tip

注意2 SAT中边 (x,y) ( x , y ) 的含义:如果选择了 x x ,我们只能选择y

#include<cstdio>
#include<cstring>
#include<iostream>

using namespace std;

const int N=20000;
struct node{
    int y,nxt;
};
node way[40010];
int n,m,st[N],tot=0,top=0,S[N];
bool col[N];

void add(int u,int w) {
    tot++;way[tot].y=w;way[tot].nxt=st[u];st[u]=tot;
}

int fan(int x) {
    if (x&1) return x+1;
    else return x-1;
}

bool paint(int x) {
    if (col[x]) return 1;
    if (col[fan(x)]) return 0;
    col[x]=1; 
    S[++top]=x;
    for (int i=st[x];i;i=way[i].nxt) 
        if (!paint(way[i].y)) return 0;   //如果染色出现了矛盾,则返回
    return 1;
}

bool solve() {
    memset(col,0,sizeof(col));
    for (int i=1;i<=n*2;i+=2) 
        if (!col[i]&&!col[i+1]) {
            top=0;
            if (!paint(i)) {
                while (top) col[S[top--]]=0; 
                if (!paint(i+1)) return 0;    //将i的对应点染色 
            }
        }
    return 1;
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++) {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,fan(y)); add(y,fan(x));
    }
    if (solve()) {
        for (int i=1;i<=n*2;i++)
            if (col[i])
                printf("%d\n",i);
    }
    else printf("NIE\n");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值