1015 Jury Compromise(难)

描述
在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;
}

 

posted on 2019-09-13 17:44  姜姜糖  阅读(223)  评论(0编辑  收藏  举报