POJ1015 Jury Compromise
题目来源:http://poj.org/problem?id=1015
题目大意:
有一个国家,法庭的判决由公众中选出的陪审团来进行。每进行一次审讯,就要选出一个陪审团。陪审团人选的选取规则如下:首先,从所有人中随机选取一些人。对于选中的这些人,控方和辩方给每个人打一个分(0到20)来表达他们是否喜欢这个人。0表示完全不喜欢,20表示特别喜欢。基于控方和辩方的两个打分,法官希望选出的陪审团对双方都最公平有利。用di表示辩方给候选人i的打分,pi表示控方给i的打分,现在要从n个候选人中选出m个人成为陪审团。用J表示{1,...,n}的一个m个元素的子集,D(J)表示sum(dk),k属于J,P(J)表示sum(pk),k属于J.选取一个J使得|D(J)-P(J)|最小。如果有多个这样的J,则选取D(J)+P(J)最大的那一个。
输入:由多个用例组成。每个用例的第一行含两个整数n和m。n为候选人数目,m为陪审团人数。1<=n<=200,1<=m<=20.m<=n.接下来的n行每行由一个整数对di和pi组成,i=1,2,...,n.n=m=0代表输入结束。
输出:对于每一个测试用例,找出最合适的陪审团。输出格式见Sample Output.注意每个陪审团成员编号前输出一个空格,每个测试用例后输出一个空白行。
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
参考了这篇解题报告:http://blog.csdn.net/lyy289065406/article/details/6671105
用了动态规划的方法。
设控辩总分之差为“控辩差”,控辩总分之和为“控辩和”。第i个候选人的控辩差为v(i),控辩和为s(i).
dp(j,k)表示取j个候选人,使其控辩差为k的所有方案中,控辩和的最大值。假设无法选出这样的j个人则令dp(j,k)=-1.(dp(j,k)不可行.)那么对k的所有可能取值计算出dp(m,k)(-20*m <= k <= 20*m),就可以找到陪审团人选了。
递推关系的建立:dp(j,k)由某个可行的方案dp(j-1,x)加入一个候选人得来。dp(j-1,x)能够推出dp(j,k)的必要条件是,存在某个i,在方案dp(j-1,x)中没有被选,且x+v(i)=k.在满足条件的所有dp(j-1,x)方案和i中,选出dp(j-1,x)+s(i)最大的那一个。
需要把每个方案选择了哪些人记录下来。将每次最后选中的那个i记录在path[j][k]中,那么方案dp(j,k)选的倒数第二个人即path[j-1][k-v[path[j][k]]].由此回溯课找到所有被选中的候选人。
初始条件:dp(0,0)=0,其它都赋为-1,逐步递推,丢出dp(m,k),这里的k的取值范围是[-20*m, 20*m],而m的最大值为20.为了方便,可以将k正向平移400,把k的取值区间映射成非负数,这样就避免了dp数组下标为负的问题。
DP完成之后,查找dp表第m行中第一个可行解即可。
再获得最终方案的控辩差和控辩和后,辩方和和控方和可由如下公式求得:
D+P=dp(m,|D-P|),
D=(D+P+|D-P|)/2, P=(D+P-|D-P|)/2.由于之前对D-P进行了平移,计算时要注意处理平移带来的影响。
1 ////////////////////////////////////////////////////////////////////////// 2 // POJ1015 Jury Compromise 3 // Memory: 424K Time: 16MS 4 // Language: C++ Result: Accepted 5 ////////////////////////////////////////////////////////////////////////// 6 7 #include <iostream> 8 #include <algorithm> 9 10 using namespace std; 11 12 int n, m; 13 int d[201], p[201], v[201], s[201]; 14 int dp[21][801]; 15 int path[21][801]; 16 int id[20]; 17 18 bool selected(int j, int k, int i) { 19 while (j > 0 && path[j][k] != i) { 20 k -= v[path[j][k]]; 21 --j; 22 } 23 return j ? false : true; 24 } 25 26 int main(void) { 27 int caseNo = 0; 28 while (true) { 29 cin >> n >> m; 30 if (n == 0) { 31 break; 32 } 33 ++caseNo; 34 for (int i = 1; i <= n; ++i) { 35 cin >> d[i] >> p[i]; 36 v[i] = d[i] - p[i]; 37 s[i] = d[i] + p[i]; 38 } 39 memset(dp, -1, sizeof(dp)); 40 memset(path, 0, sizeof(path)); 41 int fix = 20 * m; 42 dp[0][fix] = 0; 43 for (int j = 1; j <= m; ++j) { 44 for (int k = 0; k <= 2 * fix; ++k) { 45 if (dp[j - 1][k] >= 0) { 46 for (int i = 1; i <= n; ++i) { 47 if (dp[j][k + v[i]] < dp[j - 1][k] + s[i]) { 48 if (selected(j - 1, k, i)) { 49 dp[j][k + v[i]] = dp[j - 1][k] + s[i]; 50 path[j][k + v[i]] = i; 51 } 52 } 53 } 54 } 55 } 56 } 57 int minDiff; 58 for (minDiff = 0; minDiff <= fix; ++minDiff) { 59 if (dp[m][fix + minDiff] != -1 || dp[m][fix - minDiff]!= -1) { 60 break; 61 } 62 } 63 int minV = dp[m][fix - minDiff] > dp[m][fix + minDiff] ? fix - minDiff : fix + minDiff; 64 cout << "Jury #" << caseNo << endl; 65 cout << "Best jury has value "; 66 //辩方总值 = (辨控和 + 辨控差 + 修正值)/ 2 67 cout << (dp[m][minV] + minV - fix) / 2 << " for prosecution and value "; 68 //控方总值 = (辨控和 - 辨控差 + 修正值)/ 2 69 cout << (dp[m][minV] - minV + fix) / 2 << " for defence:" << endl; 70 for (int j = m, k = minV, i = 0; i < m; ++i) { 71 id[i] = path[j][k]; 72 k -= v[path[j][k]]; 73 --j; 74 } 75 sort(id, id + m); 76 for (int i = 0; i < m; ++i) { 77 cout << " " << id[i]; 78 } 79 cout << endl << endl; 80 } 81 system("pause"); 82 return 0; 83 }