题目大意: 有 n n n 个团队,每个团队两个人,现在要组成一个 n n n 人的组织,要求每个团队中只能有 1 1 1 个人在组织里,给出 m m m 组憎恶关系,相互憎恶的两人不能同时在组织里,给出一种组成组织的方案。
题解
2 − S A T 2-SAT 2−SAT 裸题,同一个团队的两人其实分别代表 0 / 1 0/1 0/1,根据憎恶关系造出图后,从编号小的团队开始跑,先跑团队中的 0 0 0,把能跑到的点都加入组织,假如和之前的方案有冲突,就尝试跑 1 1 1,如果依然有冲突,那么无解。
由于之前做的决策不会影响到答案的可行性,所以每次只考虑调整自己而不需要考虑前面的决策。具体来说就是不存在这类情况:前面团队 i i i 选了 0 0 0,枚举到 j j j 的时候发现不管选 0 0 0 还是 1 1 1 都和 i i i 有冲突,即从 j 0 j_0 j0 和 j 1 j_1 j1 出发都会走到 i 1 i_1 i1。
看起来好像是之前的决策出了问题,即团队 i i i 不应该选 0 0 0 而应该选 1 1 1,但事实上,考虑 2 − S A T 2-SAT 2−SAT 的建边规则,然后手玩一下就会发现,如果 j 0 j_0 j0 和 j 1 j_1 j1 都能走到 i 1 i_1 i1,那么 i 0 i_0 i0 就能同时走到 j 0 j_0 j0 和 j 1 j_1 j1,在这种情况下,团队 i i i 不可能选 0 0 0。
于是代码如下:
#include <cstdio>
#include <cstring>
#define maxn 20010
int n,m;
struct edge{int y,next;};
edge e[maxn<<1];
int first[maxn],len=0;
void buildroad(int x,int y){e[++len]=(edge){y,first[x]};first[x]=len;}
int kk(int x){return x%2==1?x+1:x-1;}
bool v[maxn];
int zhan[maxn],t;
bool dfs(int x)
{
if(v[x])return true;
if(v[kk(x)])return false;
v[x]=true;zhan[++t]=x;
for(int i=first[x];i;i=e[i].next)
if(!dfs(e[i].y))return false;
return true;
}
bool two_SAT()
{
for(int i=1;i<=2*n;i++)
{
if(v[i]||v[kk(i)])continue;
t=0;
if(!dfs(i))
{
while(t)v[zhan[t--]]=false;
if(!dfs(kk(i)))return false;
}
}
return true;
}
int main()
{
while(~scanf("%d",&n))
{
scanf("%d",&m);
memset(first,0,sizeof(first));len=0;
for(int i=1,x,y;i<=m;i++)scanf("%d %d",&x,&y),
buildroad(x,kk(y)),buildroad(y,kk(x));
memset(v,false,sizeof(v));
if(two_SAT())
{
for(int i=1;i<=2*n;i++)
if(v[i])printf("%d\n",i);
}
else printf("NIE\n");
}
}