[hdu 1814] Peaceful Commission(2-sat求最小序列可行解)

这道题求的是可行解中的最小序列
我们给结点染色,假设白色是未染色,红色是要选取的结点,蓝色是抛弃的结点。
首先从第一个点开始染色,染成红色,同时将同组另一节点染成蓝色,然后将这个点的所有后继结点也染成红色,同时开一个数组记录都染了哪些结点。如果后来发现某结点的后继是蓝色,说明本次染色失败,因为碰到了矛盾(假如一个结点被选取,那么所有后继肯定也得全部选取)。那么因为刚才染色的时候记录了染色的结点,靠这个数组将结点全部还原回白色。然后从第二个结点开始探索,直到全部染色完毕,最后的红色结点就是答案。

#include<iostream>
#include<vector>
#include<cstdio>
using namespace std;

#define maxn 20000
#define W 0
#define R 1
#define B 2
int n, m;
vector<int> mp[maxn];
int color[maxn];
// 用于存储尝试染色的结点
// 若失败则全部还原回白色
int temp[maxn], num;

bool dfs(int u)
{
if(color[u] == R)
return true;
if(color[u] == B)
return false;
// 结点未染色,试着染成红色并记录
color[u] = R;
// 同组另一结点肯定淘汰了,蓝色
color[((u-1)^1)+1] = B;
temp[num++] = u;
// 按照当前节点继续染色,看看是否会冲突
// 冲突则代表当前这种染色办法不行
// 那么就凭刚才做记录的temp数组还原结点(染白)
for(int i = 0; i < mp[u].size(); i++)
{
if(!dfs(mp[u][i]))
return false;
}
return true;
}

bool solve()
{
memset(color, W, sizeof(color));
for(int i = 1; i <= 2*n; i++)
{
// 此节点未访问过,试着染色
if(color[i] == W)
{
num = 0;
// 若染色失败
if(!dfs(i))
{
// 凭着染色时的记录,全部还原回白色
for(int j = 0; j < num; j++)
{
color[temp[j]] = W;
color[((temp[j]-1)^1)+1] = W;
}
// 如果同组另一节点又失败了
// 同组两个都不能选,无解
if(!dfs(((i-1)^1)+1))
return false;
}
}
}
return true;
}

int main()
{
int a, b;
while(~scanf("%d%d", &n, &m))
{
for(int i = 1; i <= 2*n; i++)
{
mp[i].clear();
}
for(int i = 1; i <= m; i++)
{
scanf("%d%d", &a, &b);
mp[a].push_back(((b-1)^1)+1);
mp[b].push_back(((a-1)^1)+1);
}

if(solve())
{
for(int i = 1; i <= 2*n; i++)
{
if(color[i] == R)
printf("%d\n", i);
}
}
else
{
printf("NIE\n");
}
}
return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值