ural 1156
初看这个题目就是个简单无向图的2-着色问题,但是本题有个特殊的要求,既用到的两种颜色的顶点数目要一样!
比较好的一道题目,首先建立一个2n个点的图,如果i和j题目类似,则在顶点i和顶点j直接连一条边。则问题转换为对顶点进行2-着色,使得红颜色的顶点数和蓝颜色的顶点数都为n。
首先求出整个图的连通分量,很明显必须每个连通分量都要能够进行朴素的2-着色,这个过程可以通过随便指定一个顶点的颜色,然后进行dfs确定其它顶点的颜色,如果该连同分量不能进行2-着色(比如存在偶环),那么很明显整个问题无解。
在对连同分量进行dfs的过程中,我们记录每个连同分量红色和蓝色的顶点的使用情况(要记录蓝色顶点有哪些,红色顶点有哪些),由于红色和蓝色是对称的!即红色换成蓝色,蓝色换成红色不影响着色,而我们最终的目的要使红色的顶点数和蓝色的顶点数都为n,这就构成一个dp过程!
设有c个连同分量,S1,S2,S3...,Sc,
连同分量Si进行2-着色使用的2中颜色的顶点数分别为Si[1]和Si[2]
ok[i][j]=true表示用对前i个连同分量进行着色颜色1使用的顶点个数为j这种情况可以发生
则有一下dp过程
ok[i][j]={ true, ok[i-1][j-Si[1]]=true or ok[i-1][j-Si[2]]=true
{ false, else
最后如果ok[c][n]=true的话,说明有解,否则误解。
注意到ok[i][...]只与ok[i-1][...]有关,这样可以用一个滚动数组,在每求出一个连同分量的时候进行一次dp状态转移就可以了。
要输出具体方案的话很简单,只需在dp过程中加如一个集合操作即可!
比较好的一道题目,首先建立一个2n个点的图,如果i和j题目类似,则在顶点i和顶点j直接连一条边。则问题转换为对顶点进行2-着色,使得红颜色的顶点数和蓝颜色的顶点数都为n。
首先求出整个图的连通分量,很明显必须每个连通分量都要能够进行朴素的2-着色,这个过程可以通过随便指定一个顶点的颜色,然后进行dfs确定其它顶点的颜色,如果该连同分量不能进行2-着色(比如存在偶环),那么很明显整个问题无解。
在对连同分量进行dfs的过程中,我们记录每个连同分量红色和蓝色的顶点的使用情况(要记录蓝色顶点有哪些,红色顶点有哪些),由于红色和蓝色是对称的!即红色换成蓝色,蓝色换成红色不影响着色,而我们最终的目的要使红色的顶点数和蓝色的顶点数都为n,这就构成一个dp过程!
设有c个连同分量,S1,S2,S3...,Sc,
连同分量Si进行2-着色使用的2中颜色的顶点数分别为Si[1]和Si[2]
ok[i][j]=true表示用对前i个连同分量进行着色颜色1使用的顶点个数为j这种情况可以发生
则有一下dp过程
ok[i][j]={ true, ok[i-1][j-Si[1]]=true or ok[i-1][j-Si[2]]=true
{ false, else
最后如果ok[c][n]=true的话,说明有解,否则误解。
注意到ok[i][...]只与ok[i-1][...]有关,这样可以用一个滚动数组,在每求出一个连同分量的时候进行一次dp状态转移就可以了。
要输出具体方案的话很简单,只需在dp过程中加如一个集合操作即可!
#include <iostream>
#include <cstdlib>
#include <set>
using namespace std;
const int maxn=50;
bool g[maxn*2+1][maxn*2+1];
int l[maxn*2+1],c[3];
bool ok[maxn+1],newok[maxn+1];
set<int> s[3];
set<int> way[maxn+1],newway[maxn+1];
int n,m;
void fail() {
cout<<"IMPOSSIBLE"<<endl;
exit(0);
}
void dfs(int x,int v) {
if(l[x]+v==3) fail();
if(l[x]==v) return;
l[x]=v;
c[v]++;
s[v].insert(x);
for(int i=1;i<=2*maxn;i++)
if(g[x][i])
dfs(i,3-v);
}
void dp() {
if(c[1]>n||c[2]>n)
fail();
memset(newok,0,sizeof newok);
for(int i=0;i<=maxn;i++) newway[i].clear();
for(int i=1;i<=2;i++) {
for(int j=0;j<=n-c[i];j++) {
if(ok[j]&&!newok[j+c[i]]) {
newok[j+c[i]]=true;
newway[j+c[i]]=way[j];
for(set<int>::iterator it=s[i].begin();it!=s[i].end();it++)
newway[j+c[i]].insert(*it);
}
}
}
for(int i=0;i<=maxn;i++) {
ok[i]=newok[i];
way[i]=newway[i];
}
}
int main() {
cin>>n>>m;
int x,y;
for(int i=1;i<=m;i++) {
cin>>x>>y;
g[x][y]=g[y][x]=true;
}
ok[0]=true;
for(int i=1;i<=2*n;i++) {
if(l[i]==0) {
c[1]=0;c[2]=0;
s[1].clear();s[2].clear();
dfs(i,1);
dp();
}
}
if(ok[n]) {
for(int i=1;i<=2*n;i++)
if(way[n].find(i)!=way[n].end())
cout<<i<<' ';
cout<<endl;
for(int i=1;i<=2*n;i++)
if(way[n].find(i)==way[n].end())
cout<<i<<' ';
cout<<endl;
}else
fail();
return 0;
}
#include <cstdlib>
#include <set>
using namespace std;
const int maxn=50;
bool g[maxn*2+1][maxn*2+1];
int l[maxn*2+1],c[3];
bool ok[maxn+1],newok[maxn+1];
set<int> s[3];
set<int> way[maxn+1],newway[maxn+1];
int n,m;
void fail() {
cout<<"IMPOSSIBLE"<<endl;
exit(0);
}
void dfs(int x,int v) {
if(l[x]+v==3) fail();
if(l[x]==v) return;
l[x]=v;
c[v]++;
s[v].insert(x);
for(int i=1;i<=2*maxn;i++)
if(g[x][i])
dfs(i,3-v);
}
void dp() {
if(c[1]>n||c[2]>n)
fail();
memset(newok,0,sizeof newok);
for(int i=0;i<=maxn;i++) newway[i].clear();
for(int i=1;i<=2;i++) {
for(int j=0;j<=n-c[i];j++) {
if(ok[j]&&!newok[j+c[i]]) {
newok[j+c[i]]=true;
newway[j+c[i]]=way[j];
for(set<int>::iterator it=s[i].begin();it!=s[i].end();it++)
newway[j+c[i]].insert(*it);
}
}
}
for(int i=0;i<=maxn;i++) {
ok[i]=newok[i];
way[i]=newway[i];
}
}
int main() {
cin>>n>>m;
int x,y;
for(int i=1;i<=m;i++) {
cin>>x>>y;
g[x][y]=g[y][x]=true;
}
ok[0]=true;
for(int i=1;i<=2*n;i++) {
if(l[i]==0) {
c[1]=0;c[2]=0;
s[1].clear();s[2].clear();
dfs(i,1);
dp();
}
}
if(ok[n]) {
for(int i=1;i<=2*n;i++)
if(way[n].find(i)!=way[n].end())
cout<<i<<' ';
cout<<endl;
for(int i=1;i<=2*n;i++)
if(way[n].find(i)==way[n].end())
cout<<i<<' ';
cout<<endl;
}else
fail();
return 0;
}