【练习试题】分组(poj1112 Team Them Up!)

来源:互联网 发布:离线地图数据库开发 编辑:程序博客网 时间:2024/06/06 11:47

【练习试题】分组(Poj1112 Team Them Up!)

Description

你的任务是把一些人分成两组,使得:
•每个人都被分到其中一组;
•每个组都至少有一个人;
•一组中的每个人都认识其他同组成员;
•两组的成员人数尽量接近。
这个问题可能有多个解决方案,你只要输出两组人数之差的绝对值即可,或者输出这样的分组法不存在。

Input

为了简单起见,所有的人都用一个整数标记,每个人号码不同,从1到N。
输入文件的第一行包括一个整数N(2≤N≤100),N就是需要分组的总人数;接下来的N行对应了这N个人,按每个人号码的升序排列,每一行给出了一串号码Aij (1≤Aij≤N,Aij≠i),代表了第i个人所认识的人的号码,号码之间用空格隔开,并以一个“0”结束。

Output

如果分组方法不存在,就输出信息“No solution”(输出时无需加引号)至输出文件;否则输出两组人数之差的绝对值。
接下来两行输出具体分组方案,每行第一个数位该组人数。

Sample Input

5
2 3 5 0
1 4 5 3 0
1 2 5 0
1 2 3 0
4 3 2 1 0

Sample Output

1
2 2 4
3 1 3 5

Solution

我们发现,两个没有朋友关系的人必定会被分到两个不同的组合。也就是说,对于这张图,我们可以
理解为一张二分图。当且仅当双方都有朋友关系时,才进行连边。我们就可以将所有的点划分为一些
相对独立的块,每一块都有人数不同的两边,这两边必定是在不同的组合之中。
当然,如果说这里并不满足二分图的性质,那么也自然无法得出符合条件的解。
我们将每一个块当作一个物品,并且进行dp,就可以判定出最优方案的个数差了。然后根据我们dp的
过程在递归求出具体的解。 
#include<iostream>#include<cstring>#include<cstdio>using namespace std;inline int read(){char c;int rec=0;while((c=getchar())<'0'||c>'9');while(c>='0'&&c<='9')rec=rec*10+c-'0',c=getchar();return rec;}int n;int map[105][105];int c[105],cnt,flag;int group[105][3],f[105][105];int belong[505][105];inline void Dfs(int v,int color,int p){if(c[v]){if(c[v]!=color)flag=1;return ;}c[v]=color;group[p][color]++;belong[p+(color-1)*n][group[p][color]]=v;for(int i=1;i<=n;i++){if(i==v||map[v][i])continue;Dfs(i,3-color,p);if(flag)break;}return ;}bool Judge(){for(int i=1;i<=n;i++){if(c[i])continue;Dfs(i,1,++cnt);if(flag)break;}return flag;}int vis[105];inline void Print(int p,int v){if(f[p-1][v-group[p][1]]){for(int i=1;i<=group[p][1];i++){vis[belong[p][i]]=1;}Print(p-1,v-group[p][1]);}else if(f[p-1][v-group[p][2]]){for(int i=1;i<=group[p][2];i++){vis[belong[p+n][i]]=1;}Print(p-1,v-group[p][2]);}return ;}void Dp(){f[0][0]=1;for(int i=1;i<=cnt;i++){for(int j=0;j<=n;j++){if(j>=group[i][1]&&f[i-1][j-group[i][1]])f[i][j]=1;if(j>=group[i][2]&&f[i-1][j-group[i][2]])f[i][j]=1;}}for(int i=n>>1;i>=0;i--)if(f[cnt][i]){cout<<n-2*i<<endl;Print(cnt,i);break;}return ;}int main(){n=read();int x;for(int i=1;i<=n;i++){while(1){x=read();if(x==0)break;map[i][x]=1;}}for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(map[i][j]==0||map[j][i]==0)    map[i][j]=map[j][i]=0;if(Judge())cout<<"No solution\n";else Dp();int tot=0;for(int i=1;i<=n;i++)tot+=vis[i];cout<<tot<<" ";for(int i=1;i<=n;i++)if(vis[i])cout<<i<<" ";cout<<endl<<n-tot<<" ";for(int i=1;i<=n;i++)if(vis[i]==0)cout<<i<<" ";return 0;}
0 0
原创粉丝点击