poj1015 Jury Compromise【背包】
Time Limit: 1000MS | Memory Limit: 65536K | |||
Total Submissions:32355 | Accepted:8722 | Special Judge |
Description
Based on the grades of the two parties, the judge selects the jury. In order to ensure a fair trial, the tendencies of the jury to favour either defence or prosecution should be as balanced as possible. The jury therefore has to be chosen in a way that is satisfactory to both parties.
We will now make this more precise: given a pool of n potential jurors and two values di (the defence's value) and pi (the prosecution's value) for each potential juror i, you are to select a jury of m persons. If J is a subset of {1,..., n} with m elements, then D(J ) = sum(dk) k belong to J
and P(J) = sum(pk) k belong to J are the total values of this jury for defence and prosecution.
For an optimal jury J , the value |D(J) - P(J)| must be minimal. If there are several jurys with minimal |D(J) - P(J)|, one which maximizes D(J) + P(J) should be selected since the jury should be as ideal as possible for both parties.
You are to write a program that implements this jury selection process and chooses an optimal jury given a set of candidates.
Input
These values will satisfy 1<=n<=200, 1<=m<=20 and of course m<=n. The following n lines contain the two integers pi and di for i = 1,...,n. A blank line separates each round from the next.
The file ends with a round that has n = m = 0.
Output
On the next line print the values D(J ) and P (J ) of your jury as shown below and on another line print the numbers of the m chosen candidates in ascending order. Output a blank before each individual candidate number.
Output an empty line after each test case.
Sample Input
4 2 1 2 2 3 4 1 6 2 0 0
Sample Output
Jury #1 Best jury has value 6 for prosecution and value 4 for defence: 2 3
Hint
Source
题意:
从n个候选人中选出m个,每个人有两个分数,分别是辩方和控方打出的。现在希望选出的这m个人,他们的辩方分数和与控方分数和之差的绝对值最小,当有多种情况时选择两个分数和最大的一种。还要输出方案。
思路:
感觉略难。
我们可以把n个候选人当做是n个物品,每个人的人数作为一维体积,装满容积为m的背包。每个候选人辩、控得分差作为体积之一,辩、控双方的得分和作为价值。dp[j][k]表示取j个候选人,使其辩控差为k的所有方案中,辩控和最大的那个方案。并且,我们还规定,如果没法选j 个人,使其辩控差为k,那么f(j, k)的值就为-1,也称方案f(j, k)不可行。本题是要求选出m 个人,那么,如果对k 的所有可能的取值,求出了所有的f(m, k) (-20×m≤ k ≤ 20×m),那么陪审团方案自然就很容易找到了。可行方案f(j-1, x)能演化成方案f(j, k)的必要条件是:存在某个候选人i,i 在方案f(j-1, x)中没有被选上,且x+V(i) = k。在所有满足该必要条件的f(j-1, x)中,选出 f(j-1, x) + S(i) 的值最大的那个,那么方案f(j-1, x)再加上候选人i,就演变成了方案 f(j, k)。这中间需要将一个方案都选了哪些人都记录下来。不妨将方案f(j, k)中最后选的那个候选人的编号,记在二维数组的元素path[j][k]中。那么方案f(j, k)的倒数第二个人选的编号,就是path[j-1][k-V[path[j][k]]。假定最后算出了解方案的辩控差是k,那么从path[m][k]出发,就能顺藤摸瓜一步步求出所有被选中的候选人。
1 //#include <bits/stdc++.h> 2 #include<iostream> 3 #include<cmath> 4 #include<algorithm> 5 #include<stdio.h> 6 #include<cstring> 7 #include<map> 8 9 #define inf 0x3f3f3f3f 10 using namespace std; 11 typedef long long LL; 12 13 int n, m; 14 const int maxn = 205; 15 const int maxm = 25; 16 int p[300], d[300], ans[30]; 17 int dp[30][1000], path[30][1000]; 18 19 int main() 20 { 21 int cas = 1; 22 while(scanf("%d %d", &n, &m) != EOF && (n || m)){ 23 24 for(int i = 1; i <= n; i++){ 25 scanf("%d%d", &p[i], &d[i]); 26 } 27 28 memset(dp, -1, sizeof(dp)); 29 memset(path, 0, sizeof(path)); 30 dp[0][20 * m] = 0; 31 for(int j = 0; j < m; j++){//j表示选出的人的数目 32 for(int k = 0; k <= m * 40; k++){ 33 if(dp[j][k] >= 0){//方案(j,k)可行 34 for(int i = 1; i <= n; i++){//找i是否出现过并且是否值得更新 35 int t1, t2; 36 if(dp[j][k] + p[i] + d[i] > dp[j + 1][k + p[i] - d[i]]){ 37 t1 = j; t2 = k; 38 while(path[t1][t2] != i && t1 > 0){ 39 t2 -= p[path[t1][t2]] - d[path[t1][t2]]; 40 t1--; 41 } 42 if(t1 == 0){ 43 dp[j + 1][k + p[i] - d[i]] = dp[j][k] + p[i] + d[i]; 44 path[j + 1][k + p[i] - d[i]] = i; 45 } 46 } 47 } 48 } 49 } 50 } 51 52 int x = m * 20, y = 0; 53 while(dp[m][x + y] < 0 && dp[m][x - y] < 0)y++; 54 int k; 55 if(dp[m][x + y] > dp[m][x - y]){ 56 k = x + y; 57 } 58 else{ 59 k = x - y; 60 } 61 62 printf("Jury #%d\n",cas++); 63 printf("Best jury has value %d for prosecution and value %d for defence:\n",(k-m*20+dp[m][k])/2,(dp[m][k]-k+m*20)/2); 64 for(int i=1;i<=m;i++) 65 { 66 ans[i]=path[m-i+1][k]; 67 k-=p[ans[i]]-d[ans[i]]; 68 } 69 sort(ans + 1, ans + m + 1); 70 for(int i=1;i<=m;i++) 71 printf(" %d",ans[i]); 72 printf("\n\n"); 73 } 74 return 0; 75 }