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过程中加如一个集合操作即可!

#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;
}


你可能感兴趣的:(r)