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 }
时间: 2025-01-02 00:45:44

UVa 1627 - Team them up!——[0-1背包]的相关文章

【暑假】[深入动态规划]UVa 1627 Team them up!

UVa 1627 Team them up! 题目: Team them up! Time Limit: 3000MS   Memory Limit: Unknown   64bit IO Format: %lld & %llu Submit Status Description Your task is to divide a number of persons into two teams, in such a way, that: everyone belongs to one of th

UVA - 10280Old Wine Into New Bottles(完全背包+剪枝)

题目:UVA - 10280Old Wine Into New Bottles(完全背包+剪枝) 题目大意:现在要将旧酒装入新瓶中,每种瓶子都有最小最大的容量要求,然后给你L升酒,在给你N个瓶子,每中瓶子的规格说明也给你,每个种类的瓶子的供应是无限的,问怎样子安排这些酒才能使得剩余的酒最少. 解题思路:这题是完全背包的题目,但是一开始就被这题的数据吓到,10^9ML,然后还有100个瓶子,瓶子的最大最小容量还相差4000左右,直接去完全背包肯定超时.之后看了大神的题接,有个规律:每个种类的瓶子的

Uva 11609 - Team ( 组合数学 + 二项式性质 + 快速幂取模 )

Uva 11609 - Team ( 组合数学 + 二项式性质 + 快速幂取模 ) 题意: 有N个人,选一个或多个人参加比赛,其中一名当队长,有多少种方案? (如果参赛者完全相同但是队长不同,也算是一种情况) [ 1<=n <= 10^9 ] 分析: 这题要用到组合式公式的性质 转化之后快速幂取模轻松搞定之 代码: //Uva 11609 - Team /* 组合数公式 + 二项式系数性质 + 快速幂 手动自己推 -> F[n] = C(n,1)*1 + C(n,2)*2 + C(n,n

poj1417 带权并查集+0/1背包

题意:有一个岛上住着一些神和魔,并且已知神和魔的数量,现在已知神总是说真话,魔总是说假话,有 n 个询问,问某个神或魔(身份未知),问题是问某个是神还是魔,根据他们的回答,问是否能够确定哪些是神哪些是魔. 对于这些问题,我们只需要发现,如果回答对方是魔,那么即可以判断出这两个不是同一种族,而如果回答对方是神,那么说明这两个是同一种族,那么就可以用带权并查集合并这些神和魔,然后记录两种分别多少个,这样当所有询问都处理完时我们就可以得到一系列的集合,每个集合分别有它的两个种族的人数,但是此时对于每个

NOJ 1860 保研(0/1背包概率dp)

保研 时间限制(普通/Java):1000MS/3000MS         运行内存限制:65536KByte 总提交:171          测试通过:40 题目描述 对于一些名校而言,保研不仅可以由学校推免,也可以由学生自己向希望保研的学校提出申请,这个过程有点类似于外国学生向学校提交简历等待Offer的过程.但是,投递申请需要亲自去相应学校的研招办递交材料,这就需要一些成本(比如路费等),且每个院校都有自己的录取成功率.现在,请在总成本不超过限制的情况下,求出最大的成功率. 输入 输入

POJ 1636 Prison rearrangement DFS+0/1背包

题目链接: POJ 1636 Prison rearrangement Prison rearrangement Time Limit: 3000MS   Memory Limit: 10000K Total Submissions: 2194   Accepted: 984 Description In order to lower the risk of riots and escape attempts, the boards of two nearby prisons of equal

POJ 3628 Bookshelf 2 0/1背包和DFS两种解法

题目链接:POJ 3628 Bookshelf 2 Bookshelf 2 Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7462   Accepted: 3436 Description Farmer John recently bought another bookshelf for the cow library, but the shelf is getting filled up quite quickly,

牛客网 TaoTao要吃鸡 ( 0/1背包变形 )

题意 : 题目链接 分析 :  如果没有 BUG (即 h == 0 的时候)就是一个普通的 0 / 1 背包 需要讨论一下 h != 0 的情况 此时有就相当于有物品是有特权的 而且背包装有特权的物品根据题目的要求是应当最后装的 也就是说特权物品装完之后背包将不再可装 所以特权物品肯定是只有一个的 数据量并不大,所以可以去枚举这个特权物品 那么如何知道在  没有装特权物品  之前的最佳选择方案? 答案就是用最小的可剩空间留给特权物品,然后其他空间去跑 0/1 背包 最小的可剩空间当然就是 h

浙大PAT CCCC L3-001 凑零钱 ( 0/1背包 &amp;&amp; 路径记录 )

题目链接 分析 : 就是一个 0/1 背包,但是需要记录具体状态的转移情况 这个可以想象成一个状态转移图,然后实际就是记录路径 将状态看成点然后转移看成边,最后输出字典序最小的路径 这里有一个很巧妙的做法 先将所有的硬币升序排序(这一点很重要) 然后在这一条件下,假设当前状态是考虑第 i 个硬币,前一个状态是考虑第 i-1 个硬币 试想对于同一个体积,如果选用的硬币数量越多是不是字典序更小 然后对于如果对于同一体积下,选用硬币数一样多的两种方案 由于我们已经升序排序,如果有一样多硬币的情况,那么