描述
在Frobnia,一个遥远的国家,法庭审判的判决由一个由公众成员组成的陪审团决定。每次试验开始时,都必须选择一个陪审团,其工作如下。首先,从公众中随机抽取几个人。对于该池中的每个人,辩护和起诉分配从0到20的等级,表明他们对此人的偏好。 0表示完全不喜欢,20表示此人被认为非常适合陪审团。
根据双方的等级,法官选出陪审团。为确保公正审判,陪审团倾向于辩护或起诉的倾向应尽可能平衡。因此,必须以对双方都满意的方式选择陪审团。
我们现在将更准确地说明:给予每个潜在陪审员i的n个潜在陪审员和两个值di(辩护的价值)和pi(起诉的价值),你将选择一个由m个人组成的陪审团。如果J是具有m个元素的{1,...,n}的子集,则D(J)= sum(dk)k属于J
而P(J)= sum(pk)k属于J,是该陪审团辩护和起诉的总价值。
对于最优陪审团J,值| D(J)-P(J)|必须是最小的。如果有几个具有最小| D(J) - P(J)|的jurys,则应该选择最大化D(J)+ P(J)的jurys,因为陪审团应该对双方都尽可能理想。
您将编写一个实施此陪审团选择过程的程序,并在给出一组候选人的情况下选择最佳陪审团。
输入
输入文件包含几个陪审团选择轮次。每一轮以包含两个整数n和m的线开始。 n是候选人的数量和陪审团成员的数量。
这些值将满足1 <= n <= 200,1 <= m <= 20,当然m <= n。以下n行包含i = 1,...,n的两个整数pi和di。每一轮与下一轮分隔一个空白行。
该文件以n = m = 0的圆形结束。
输出
每轮输出一行包含陪审团选择轮次('陪审团#1','陪审团#2'等)。
在下一行打印您的陪审团的值D(J)和P(J),如下所示,在另一行打印m选择的候选人的数字按升序排列。在每个候选人号码前输出一个空格。
在每个测试用例后输出一个空行。
题目要求繁杂,需要耐心提炼题目条件。
本题主要求四个不同数组中其中两个和的di[]-pi[]最小,且当最小值一定时pi[]+di[]最大。
首先输入数据。
样例输入
4 2 1 2 2 3 4 1 6 2 0 0
样例输出
Jury #1 Best jury has value 6 for prosecution and value 4 for defence: 2 3
思路:
本题可以看成一个背包问题,背包容积为m,有n个人,各自体积分成di,pi。dp[i][k]表示选择i个人中控差值为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)。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #define rg register #define go(i,a,b) for(rg int i=a;i<=b;i++) #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; int n,m,dp[25][802]; int p[202],d[202],S[202],V[202]; int tag[25][802]; //tag[i][j]表示选了i个人分数为j时第i个人的编号 bool check(int a,int b,int c){//判断c是否被选过 while(a>0&&c!=tag[a][b]){ b-=V[tag[a][b]];a--; } return a; } int main(){ int num=0; while(scanf("%d%d",&n,&m)){ if(n==0&&m==0) break; go(i,1,n){ scanf("%d%d",&p[i],&d[i]); S[i]=p[i]+d[i]; V[i]=p[i]-d[i]; } memset(dp,-1,sizeof(dp)); //dp[i][j]表示选了i个人使分数差值为j的最大和,如不存在则为-1 memset(tag,0,sizeof(tag)); int start=20*m; dp[0][start]=0;//相当于dp[0][0] go(i,1,m) go(j,0,2*start) if(dp[i-1][j]!=-1)//能够选到i-1个人使便控差为j; go(k,1,n) if(!check(i-1,j,k)&&dp[i][j+V[k]]<dp[i-1][j]+S[k]){ dp[i][j+V[k]]=dp[i-1][j]+S[k]; tag[i][j+V[k]]=k; } int j; for(j=0;j<=start;j++) if(dp[m][start-j]!=-1||dp[m][start+j]!=-1) break; int sum=dp[m][start-j]>dp[m][start+j]?start-j:start+j;//sum为差值,start为本来的0值,+j或者-j就是前后的比较 int P=(dp[m][sum]+sum-start)/2,D=(dp[m][sum]-sum+start)/2;//sum为符合条件的差值,sum-start为原本没有润色过的差值 printf("Jury #%d\n",++num); printf("Best jury has value %d for prosecution and value %d for defence:\n",P,D); int res[25]; for(rg int i=0,j=m,k=sum;i<m;i++){ res[i]=tag[j][k]; k-=V[tag[j][k]]; j--; } sort(res,res+m); go(i,0,m-1) printf(" %d",res[i]); puts("");puts(""); } return 0; }