POJ 1112 Team Them Up!【二分图染色+DP】
题意: 有 n 个人,他们之间的关系有四种,给出一些关系 a,b 表示b 知道 a,现在想把这些人分成两组,每个组里面所有人都相互知道,如果可以分成这两组,
找出两组人数相差最少的情况。
分析:如果 a 和 b 不是相互知道,就在a,b之间连一条双向边,表示a 和b 绝不能分在一个组里
建好图之后,进行染色,判断是否是二分图,如果不是二分图,肯定不存在符合条件的情况
染色的同时,记录每个连通块中每个部分的个数,并记录路径
用 01 背包标记所有存在的状态,找到差值最小的情况
#include<stdio.h> #include<string.h> #define clr(x)memset(x,0,sizeof(x)) #define maxn 110 int g[maxn][maxn]; int r[maxn][maxn]; int dp[maxn][maxn]; int c[maxn]; int num[maxn][2]; int f[maxn]; int flag; int sn,d,n; void dfs(int x) { int i; for(i=1;i<=n;i++) if(r[x][i]) { if(c[x]==c[i]) { flag=1; return; } else if(c[i]!=-1) continue; f[i]=sn; c[i]=c[x]^1; num[sn][c[i]]++; dfs(i); f[i]=sn; if(flag==1) return; } } int main() { int i,j,p; while(scanf("%d",&n)!=EOF) { clr(g); clr(r); clr(f); clr(num); clr(f); clr(dp); memset(c,-1,sizeof(c)); for(i=1;i<=n;i++) { while(scanf("%d",&p),p) g[i][p]=1; } for(i=1;i<=n;i++) for(j=1;j<=n;j++) { if(g[i][j]&&g[j][i]) continue; if(i==j) continue; r[i][j]=r[j][i]=1; } sn=1; flag=0; for(i=1;i<=n;i++) if(c[i]==-1) { c[i]=0; num[sn][0]++; f[i]=sn; dfs(i); if(flag==1) { printf("No solution\n"); break; } sn++; } if(flag) return 0; dp[0][0]=1; for(i=1;i<sn;i++) for(j=0;j<=n/2;j++) { if(dp[i-1][j]) { if(j+num[i][0]<=n/2) dp[i][j+num[i][0]]=1; if(j+num[i][1]<=n/2) dp[i][j+num[i][1]]=1; } } int tmp,cn; for(i=n/2;i>=0;i--) { if(dp[sn-1][i]==1) { tmp=i; cn=i; break; } } int vis[maxn]; clr(vis); for(i=sn-1;i>=1;i--) { int tt; if(tmp-num[i][0]>=0&&dp[i-1][tmp-num[i][0]]) { tt=0; tmp=tmp-num[i][0]; } else if(tmp-num[i][1]>=0&&dp[i-1][tmp-num[i][1]]) { tmp=tmp-num[i][1]; tt=1; } for(j=1;j<=n;j++) if(f[j]==i&&c[j]==tt) vis[j]=1; } printf("%d ",cn); for(i=1;i<=n;i++) if(vis[i]) printf("%d ",i); putchar('\n'); printf("%d ",n-cn); for(i=1;i<=n;i++) if(!vis[i]) printf("%d ",i); putchar('\n'); } return 0; }