Peaceful Commission
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3679 Accepted Submission(s): 1224
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.
There are multiple test cases. Process to end of file.
3 2 1 3 2 4
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:其中在②③步操作中,出现以下情况之一,则说明无解(无法构成议会)
- 某个已被加入队列q1的点被加入队列q2;
- 某个已被加入队列q2的点被加入队列q1;
- 某个必选点i的后继j的状态为2;
- 某个不选点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;
}