HDU 1814:Peaceful Commission(2-SAT的字典序最小解)

Peaceful Commission

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3679    Accepted Submission(s): 1224


Problem Description
The Public Peace Commission should be legislated in Parliament of The Democratic Republic of Byteland according to The Very Important Law. Unfortunately one of the obstacles is the fact that some deputies do not get on with some others. 

The Commission has to fulfill the following conditions: 
1.Each party has exactly one representative in the Commission, 
2.If two deputies do not like each other, they cannot both belong to the Commission. 

Each party has exactly two deputies in the Parliament. All of them are numbered from 1 to 2n. Deputies with numbers 2i-1 and 2i belong to the i-th party . 

Task 
Write a program, which: 
1.reads from the text file SPO.IN the number of parties and the pairs of deputies that are not on friendly terms, 
2.decides whether it is possible to establish the Commission, and if so, proposes the list of members, 
3.writes the result in the text file SPO.OUT. 
 

Input
In the first line of the text file SPO.IN there are two non-negative integers n and m. They denote respectively: the number of parties, 1 <= n <= 8000, and the number of pairs of deputies, who do not like each other, 0 <= m <=2 0000. In each of the following m lines there is written one pair of integers a and b, 1 <= a < b <= 2n, separated by a single space. It means that the deputies a and b do not like each other. 
There are multiple test cases. Process to end of file. 
 

Output
The text file SPO.OUT should contain one word NIE (means NO in Polish), if the setting up of the Commission is impossible. In case when setting up of the Commission is possible the file SPO.OUT should contain n integers from the interval from 1 to 2n, written in the ascending order, indicating numbers of deputies who can form the Commission. Each of these numbers should be written in a separate line. If the Commission can be formed in various ways, your program may write mininum number sequence. 
 

Sample Input
  
  
3 2 1 3 2 4
 

Sample Output
  
  
1 4 5


问题概述:

一个国家有n个小组,其中每个小组刚好有两个人(第i组两个人的编号是i*2-1, i*2),现在国家要从每个小组中

选出恰好1个人以构成n人议会,但有n对约束关系(x,y)表示编号为x和y的两个人不能同时被选入议会,问能不能选出n

个人构成议会,如果可以输出字典序最小的解,若不行输出"NIE"


建模:

建立一个拥有2*n个点的无向图,如果存在边(i,j)表示如果i被选中,那么j一定要被选中

例如此题,x和y两个人不能同时被选入议会,这样的话如果x必须要选,和y一组的另一个人一定要被选入议会,

而刚刚好这另一个人的编号是y^1,这样的话就必须连一条x到y^1的边,同理也要连一条y到x^1的边

O(n*m)算法:求字典序最小的解:(若图中i到j有路径,则若i选,则j也要选;或者说,若j不选,则i也不能选)

变量解释:

V[x]:V[x]==1表示x点必选,V[x]==2表示x点不能选,V[x]==0表示未确定,初始化全为0

队列q:q1存放的是本次尝试中所有必选的点,q2存放的是本次尝试中所有不能选的点

vis[x]:用来标记是否进了队列,同时用来判断x是本次尝试中必选的点还是不能选的点

(算法中为负表示不能选,为正表示必选)

步骤:

①从第一个点开始做2*n次尝试,每次如果当前点不确定,就假设当前点必选,然后进行搜索

②搜索过程中对于队列q1中的点i找到i的所有后继,对于后继j,若j未确定,则将j加入队列q1;若j^1未确定,则

将j^1加入队列q2

③遍历q2中的每个点,找到该点的所有前驱(这里要先建一个补图),若该前驱未确定,则将其加入队列q2;

④若本次尝试成功,则将q1中的所有点的状态V改为1,将q2中所有点的状态V改为2,继续步骤①

⑤若在①的过程中所有的点均已确定,则找到一组解,该解一定字典序最小

PS:其中在②③步操作中,出现以下情况之一,则说明无解(无法构成议会)

    1. 某个已被加入队列q1的点被加入队列q2;
    2. 某个已被加入队列q2的点被加入队列q1;
    3. 某个必选点i的后继j的状态为2;
    4. 某个不选点j的前驱i的状态为1;


#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
using namespace std;
vector<int> G1[20005], G2[20005];
queue<int> q1, q2;
int V[20005], vis[20005], ans[20005][2];
int main(void)
{
	int n, m, a, b, i, j, ok, oth, now, len, flag, x, k;
	while(scanf("%d%d", &n, &m)!=EOF)
	{
		n *= 2;
		for(i=0;i<n;i++)
		{
			G1[i].clear();
			G2[i].clear();
		}
		for(i=1;i<=m;i++)
		{
			scanf("%d%d", &a, &b);
			a--, b--;
			if(a==(b^1))
				continue;
			G1[a].push_back(b^1);
			G1[b].push_back(a^1);
			G2[b^1].push_back(a);
			G2[a^1].push_back(b);
		}
		memset(V, 0, sizeof(V));
		memset(vis, 0, sizeof(vis));
		ok = 1;
		for(i=0;i<n;i+=2)
		{
			if(V[i+1]==1 || V[i]==1)
				continue;
			for(oth=0;oth<=1;oth++)
			{
				now = i+oth, len = 0;
				if(V[now])
					continue;
				ans[++len][0] = now, ans[len][1] = 1;
				if(V[now^1]==0)
					ans[++len][0] = now^1, ans[len][1] = 2;
				while(q1.empty()==0)  q1.pop();
				while(q2.empty()==0)  q2.pop();
				q1.push(now), q2.push(now^1);
				vis[now] = now+1, vis[now^1] = -now-1;
				flag = 1;
				while(q1.empty()==0)
				{
					x = q1.front();
					q1.pop();
					for(j=0;j<G1[x].size();j++)
					{
						k = G1[x][j];
						if(V[k]==2 || V[k^1]==1 || vis[k]==-now-1 || vis[k^1]==now+1)
						{
							flag = 0;
							break;
						}
						if(vis[k]!=now+1)
						{
							vis[k] = now+1;
							q1.push(k);
							if(V[k]==0)
								ans[++len][0] = k, ans[len][1] = 1;
						}
						if(vis[k^1]!=-now-1)
						{
							vis[k^1] = -now-1;
							q2.push(k^1);
							if(V[k^1]==0)
								ans[++len][0] = k^1, ans[len][1] = 2;
						}
					}
					if(flag==0)
						break;
				}
				if(flag==0)
					continue;
				while(q2.empty()==0)
				{
					x = q2.front();
					q2.pop();
					for(j=0;j<G2[x].size();j++)
					{
						k = G2[x][j];
						if(V[k]==1 || vis[k]==now+1)
						{
							flag = 0;
							break;
						}
						if(vis[k]!=-now-1)
						{
							vis[k] = -now-1;
							q2.push(k);
							if(V[k]==0)
								ans[++len][0] = k, ans[len][1] = 2;
						}
					}
					if(flag==0)
						break;
				}
				if(flag==0)
					continue;
				for(j=1;j<=len;j++)
					V[ans[j][0]] = ans[j][1];
			}
			if(V[i]+V[i^1]!=3)
			{
				ok = 0;
				printf("NIE\n");
				break;
			}
		}
		if(ok)
		{
			for(i=0;i<n;i++)
			{
				if(V[i]==1)
					printf("%d\n", i+1);
			}
		}
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值