题意:就是有n党派,每个党派两个人,只能够从中选择一个人进入委员会。同时,有m组关系,表示某两个人不能够同时出现在委员会中。求出字典序最小的委员会名单。
题解:一个2-sat问题,因为要求出最小字典序,只能够用最暴力的方法,时间复杂度为O(nm)。
通过2-sat问题构图
1.首先对当前点x进行染色,染为可行,其党派内的对应结点x’则染为不可行。
2.访问所有和x相连的结点vi,依次进行搜索。
3.如果vi未被染过颜色,同x的染色一样继续进行。如果vi已经被染过色,那么检查它是否可行。如果产生了矛盾,则证明当前染色是错误的,需退回将x’染为可行,x不可行(即重新对x’进行搜索,work()函数中的Paint(Oth(x))语句的含义 )。
4.如果重新尝试染Paint(x’)也不行,则意味着无解,退出函数并输出,否则继续依次尝试染色。
5.如果所有结点都被染上了颜色,程序结束,访问所有结点,输出所有被染为可行的结点
代码一:使用vector+染色判断
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
#define oth(x) (x%2==0?x-1:x+1)
const int maxn=8005;
int n,m;
vector<int>e[maxn*2];
int col[maxn*2];
int cnt,ans[maxn*2];
bool paint(int x)
{
if(col[x]!=0){
return col[x]%2;
}
col[x]=1;col[oth(x)]=2;
ans[++cnt]=x;
for(int i=0;i<e[x].size();i++){
if(!paint(e[x][i])){
return false;
}
}
return true;
}
bool work()
{
memset(col,0,sizeof(col));
for(int i=1;i<=2*n;i+=2){
if(col[i]||col[oth(i)]){
continue;
}
cnt=0;
if(!paint(i)){
for(int j=1;j<=cnt;j++){
col[ans[j]]=col[oth(ans[j])]=0;
}
if(!paint(oth(i))){
return false;
}
}
}
return true;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF){
for(int i=1;i<=2*n;i++){
e[i].clear();
}
int u,v;
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
e[u].push_back(oth(v));
e[v].push_back(oth(u));
}
if(work()){
for(int i=1;i<=2*n;i++){
if(col[i]==1){
printf("%d\n",i);
}
}
}else{
printf("NIE\n");
}
}
return 0;
}
代码二:使用链式前向星+染色判断
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=8010*2;
const int maxm=80010;
struct edge{
int to,next;
};
edge edges[maxm<<1];
int head[maxn],tot;
void add_edge(int u,int v)
{
edges[tot].to=v;
edges[tot].next=head[u];
head[u]=tot++;
}
int n,m;
int cnt,s[maxn];
bool mark[maxn];
bool dfs(int u)
{
if(mark[u^1]){
return false;
}
if(mark[u]){
return true;
}
s[cnt++]=u;
mark[u]=1;
for(int i=head[u];~i;i=edges[i].next){
if(!dfs(edges[i].to)){
return false;
}
}
return true;
}
bool twosat()
{
memset(mark,0,sizeof(mark));
for(int i=0;i<2*n;i+=2){
if(!mark[i]&&!mark[i+1]){
cnt=0;
if(!dfs(i)){
while(cnt){
mark[s[--cnt]]=false;
}
if(!dfs(i^1)){
return false;
}
}
}
}
return true;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF){
tot=0;
memset(head,-1,sizeof(head));
int u,v;
for(int i=0;i<m;i++){
scanf("%d%d",&u,&v);
u--;v--;
add_edge(u,v^1);
add_edge(v,u^1);
}
if(twosat()){
for(int i=0;i<2*n;i+=2){
if(mark[i]){
printf("%d\n",i+1);
}else{
printf("%d\n",i+2);
}
}
}else{
printf("NIE\n");
}
}
return 0;
}