UVa 1627 - Team them up!——[0-1背包]
Posted kiraa
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UVa 1627 - Team them up!——[0-1背包]相关的知识,希望对你有一定的参考价值。
Your task is to divide a number of persons into two teams, in such a way, that:
- everyone belongs to one of the teams;
- every team has at least one member;
- every person in the team knows every other person in his team;
- teams are as close in their sizes as possible.
This task may have many solutions. You are to find and output any solution, or to report that the solution does not exist.
Input
The input begins with a single positive integer on a line by itself indicating the number of the cases following, each of them as described below. This line is followed by a blank line, and there is also a blank line between two consecutive inputs.
For simplicity, all persons are assigned a unique integer identifier from 1 to N.
The first line in the input file contains a single integer number N (2 ≤ N ≤ 100) - the total number of persons to divide into teams, followed by N lines - one line per person in ascending order of their identifiers. Each line contains the list of distinct numbers Aij (1 ≤ Aij ≤ N, Aij ≠ i) separated by spaces. The list represents identifiers of persons that ith person knows. The list is terminated by 0.
Output
For each test case, the output must follow the description below. The outputs of two consecutive cases will be separated by a blank line.
If the solution to the problem does not exist, then write a single message "No solution" (without quotes) to the output file. Otherwise write a solution on two lines. On the first line of the output file write the number of persons in the first team, followed by the identifiers of persons in the first team, placing one space before each identifier. On the second line describe the second team in the same way. You may write teams and identifiers of persons in a team in any order.
Sample Input
2 5 3 4 5 0 1 3 5 0 2 1 4 5 0 2 3 5 0 1 2 3 4 0 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
No solution 3 1 3 5 2 2 4
题意分析:
以“不相互认识关系”建无向图,每个连通块都可以分成两队,如果存在已经分成一队的两个人互相不认识,说明无解,表现在图中就是存在环,用bfs求连通块即可。
将每个连通块中必须分到不同队的两组人分别记录下来team0,team1,接下来对求出的各个连通块动态规划,两队人数之差 dt=team0-team1,则要选择将每个连通块适当分组使dt最接近0。
以d(i,j)表示已经选择前i个连通块,且dt=j时的最优解。
状态转移方程为 d(i,j)=min{d(i-1,j-dt[i])+dt[i],d(i-1,j+dt[i]-dt[i])},其中dt[i]是指第i个连通块的两队人数差。
用b(i,j)记录当前状态应该分组的方式,0表示将连通块的team0部分分到0组,1则表示将team0部分分到1组。
代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <algorithm> 6 #include <vector> 7 using namespace std; 8 const int maxn=110; 9 #define INF 100000000 10 int know[maxn][maxn]; 11 int unknow[maxn][maxn]; 12 int team[maxn]; 13 int n; 14 vector<int> block[maxn][2]; 15 int num; 16 int d[maxn][2*maxn]; 17 int b[maxn][2*maxn]; 18 bool dfs(int i,int t,vector<int> *b){ 19 team[i]=t; 20 b[t].push_back(i); 21 bool ok=true; 22 for(int j=1;j<=n;j++){ 23 if(unknow[i][j]){ 24 if(team[j]==-1) { 25 if(!dfs(j,!t,b)){ 26 ok=false; 27 break; 28 } 29 } 30 else if(team[j]!=t) continue; 31 else{ 32 ok=false; 33 break; 34 } 35 } 36 } 37 return ok; 38 } 39 bool blocks(){ 40 for(int i=0;i<maxn;i++) 41 for(int j=0;j<2;j++) 42 block[i][j].clear(); 43 num=0; 44 for(int i=1;i<=n;i++) 45 if(team[i]==-1) 46 if(!dfs(i,0,block[num++])) 47 return false; 48 return true; 49 } 50 void dp(){ 51 memset(d, 0, sizeof d); 52 memset(b,0,sizeof b); 53 for(int i=0;i<=num;i++){ 54 for(int j=0;j<2*maxn;j++){ 55 if(i==0){ 56 d[i][j]=INF; 57 if(j==maxn) 58 d[i][j]=0; 59 continue; 60 } 61 d[i][j]=INF; 62 int deta=block[i-1][0].size()-block[i-1][1].size(); 63 if(j-deta>=0&&j-deta<2*maxn&&d[i-1][j-deta]!=INF){ 64 if(abs(d[i][j])>abs(d[i-1][j-deta]+deta)){ 65 d[i][j]=d[i-1][j-deta]+deta; 66 b[i][j]=0; 67 } 68 } 69 if(j+deta>=0&&j+deta<2*maxn&&d[i-1][j+deta]!=INF){ 70 if(abs(d[i][j])>abs(d[i-1][j+deta]-deta)){ 71 d[i][j]=d[i-1][j+deta]-deta; 72 b[i][j]=1; 73 } 74 } 75 76 } 77 } 78 } 79 void print(){ 80 int ans=INF,p=0; 81 for(int k=0;k<2*maxn;k++){ 82 if(abs(ans)>abs(d[num][k])){ 83 ans=d[num][k]; 84 p=k; 85 } 86 } 87 vector<int> team[2]; 88 for(int i=num,j=p;i>=0;i--){ 89 int dt=block[i-1][0].size()-block[i-1][1].size(); 90 int t=b[i][j]; 91 if(t==0){ 92 for(int k=0;k<block[i-1][0].size();k++) 93 team[0].push_back(block[i-1][0][k]); 94 for(int k=0;k<block[i-1][1].size();k++) 95 team[1].push_back(block[i-1][1][k]); 96 j-=dt; 97 } 98 else{ 99 for(int k=0;k<block[i-1][0].size();k++) 100 team[1].push_back(block[i-1][0][k]); 101 for(int k=0;k<block[i-1][1].size();k++) 102 team[0].push_back(block[i-1][1][k]); 103 j+=dt; 104 } 105 } 106 printf("%d",int(team[0].size())); 107 for(int i=0;i<team[0].size();i++){ 108 printf(" %d",team[0][i]); 109 } 110 printf("\n"); 111 printf("%d",int(team[1].size())); 112 for(int i=0;i<team[1].size();i++){ 113 printf(" %d",team[1][i]); 114 } 115 printf("\n"); 116 } 117 int main(int argc, const char * argv[]) { 118 int tt; 119 scanf("%d",&tt); 120 int cas=0; 121 while(tt--){ 122 memset(know,0,sizeof know); 123 memset(unknow,0,sizeof know); 124 memset(team,-1,sizeof team); 125 126 scanf("%d",&n); 127 for(int i=1;i<=n;i++){ 128 int j; 129 while(scanf("%d",&j)&&j){ 130 know[i][j]=1; 131 } 132 } 133 134 for(int j=1;j<=n;j++){ 135 for(int i=1;i<=n;i++){ 136 if(i==j) 137 continue; 138 if(!know[i][j]){ 139 unknow[i][j]=unknow[j][i]=1; 140 } 141 } 142 } 143 144 if(!blocks()){ 145 printf("No solution\n"); 146 if(tt) printf("\n"); 147 continue; 148 } 149 dp(); 150 print(); 151 if(tt) printf("\n"); 152 } 153 154 return 0; 155 }
以上是关于UVa 1627 - Team them up!——[0-1背包]的主要内容,如果未能解决你的问题,请参考以下文章
UVA1627-Team them up!(二分图判断+动态规划)