题意:
给n个人和一些认识关系,要将这n个人分成两队,每队的人之间都互相认识,求一种方案使两队的人数差最小。
分析:
对原图求逆得到新图g,g中如果有边(a,b),那么a,b不能在一个队,对新图进行二分图染色就能求得一种方案了。但题目要使人数差最小,所以还要dp一下。dp[i][j]表示前i个连通分量获得j个人的队伍是否可行,这其实是个背包问题,每个物品有多种重量,问是否能获得一个特定的重量和。
代码:
//poj 1112 //sep9 #include <iostream> using namespace std; const int maxN=128; bool g[maxN][maxN],dp[maxN][maxN/2]; int n,cnt; int color[maxN],p[maxN][2][maxN],num[maxN][2],path[maxN][maxN]; int dfs(int u) { int col=color[u]; int index=num[cnt][col]; ++num[cnt][col]; p[cnt][col][index]=u; for(int i=1;i<=n;++i) if(g[u][i]==true){ if(color[i]==-1){ color[i]=col^1; if(!dfs(i)) return false; } else if(color[i]==color[u]) return false; } return true; } int main() { int i,j,x,tmp,res; scanf("%d",&n); memset(g,false,sizeof(g)); for(i=1;i<=n;++i) while(scanf("%d",&x)==1&&x) g[i][x]=true; for(i=1;i<=n;++i) for(j=i+1;j<=n;++j){ if(g[i][j]&&g[j][i]) g[i][j]=g[j][i]=false; else g[i][j]=g[j][i]=true; } cnt=0; memset(color,-1,sizeof(color)); memset(num,0,sizeof(num)); for(i=1;i<=n;++i){ if(color[i]==-1){ color[i]=0; ++cnt; if(!dfs(i)){ printf("No solution"); return 0; } } } memset(dp,false,sizeof(dp)); dp[0][0]=true; for(i=1;i<=cnt;++i) for(j=0;j<=n/2;++j){ tmp=j-num[i][0]; if(tmp>=0&&dp[i-1][tmp]){ dp[i][j]=true; path[i][j]=0; } tmp=j-num[i][1]; if(tmp>=0&&dp[i-1][tmp]){ dp[i][j]=true; path[i][j]=1; } } int ans,col; for(i=n/2;i>0;--i) if(dp[cnt][i]) break; ans=i; tmp=i; printf("%d",ans); for(i=cnt;i>=1;--i){ if(path[i][ans]==0){ col=0; ans-=num[i][0]; }else{ col=1; ans-=num[i][1]; } for(j=0;j<num[i][col];++j) printf(" %d",p[i][col][j]); } ans=tmp; printf("\n%d",n-ans); for(i=cnt;i>=1;--i){ if(path[i][ans]==0){ col=0; ans-=num[i][0]; }else{ col=1; ans-=num[i][1]; } for(j=0;j<num[i][col^1];++j) printf(" %d",p[i][col^1][j]); } return 0; }
poj 1112 Team Them Up! 二分图染色+dp
原文地址:http://blog.csdn.net/sepnine/article/details/41979943