poj1015 Jury Compromise 题解报告

题目传送门

【题目大意】

要从n个候选人中选出m人作为陪审团,对于这n个候选人,每个人都有两个分数,一个是辩护方的分数,一个是起诉方的分数。要求一种方案,使得辩护方分数之和与起诉方分数之和的差最小而和最大。求这种方案下辩护方分数和起诉方分数。

【思路分析】

我们可以把这道题目看做是具有多个“体积维度”的0/1背包问题。把n个候选人看做n个物品,那么每个物品有以下三种“体积”:

1.“人数”,每个候选人的“人数”体积都是1,最终要填满容积为m的背包

2.“辩方得分”,即辩方给候选人打的分数,记为a[i]

3.“控方得分”,即控方给候选人打的分数,记为b[i]

so我们依次考虑每个候选人是否进入陪审团,当考虑到第i个人时,f[j][d][p]表示已经有j个人被选入陪审团,当前辩方总分为d,控方总分为p的状态是否可行。$$f[j][d][p]=f[j][d][p]orf[j-1][d-a[i]][p-b[i]]$$初始值:f[0][0][0]=true,其余均为false。

目标:找到一个状态f[m][d][p],满足f[m][d][p]=true,并且|d-p|最小,同时d+p最大。

接下来我们考虑一下如何优化,我们可以把|d-p|作为一个维度,并且用f[j][k]表示前i个人中选出j个,保证|d-p|等于k时d+p的最大值。

于是得到新的转移方程:$$f[j][k]=max(f[j][k],f[j-1][k-(a[i]-b[i])]+a[i]+b[i])$$初始值:f[0][0]=0,其余均为负无穷

目标:找到一个状态f[j][k]满足|k|尽量小,当|k|相同时f[j][k]尽量大。

在转移中要对j倒序循环,这样才符合0/1背包的特殊做法。

【代码实现】

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #define rg register
 6 #define go(i,a,b) for(rg int i=a;i<=b;i++)
 7 #define mem(a,b) memset(a,b,sizeof(a))
 8 using namespace std;
 9 int n,m,dp[25][802];
10 int p[202],d[202],S[202],V[202];
11 int tag[25][802];
12 //tag[i][j]表示选了i个人分数为j时第i个人的编号
13 bool check(int a,int b,int c){//判断c是否被选过
14     while(a>0&&c!=tag[a][b]){
15         b-=V[tag[a][b]];a--;
16     }
17     return a;
18 }
19 int main(){
20     int num=0;
21     while(scanf("%d%d",&n,&m)){
22         if(n==0&&m==0) break;
23         go(i,1,n){
24             scanf("%d%d",&p[i],&d[i]);
25             S[i]=p[i]+d[i];
26             V[i]=p[i]-d[i];
27         }
28         memset(dp,-1,sizeof(dp));
29 //dp[i][j]表示选了i个人使分数差值为j的最大和,如不存在则为-1
30         memset(tag,0,sizeof(tag));
31         int start=20*m;
32         dp[0][start]=0;//相当于dp[0][0]
33         go(i,1,m) go(j,0,2*start)
34             if(dp[i-1][j]!=-1)
35                 go(k,1,n)
36                     if(!check(i-1,j,k)&&dp[i][j+V[k]]<dp[i-1][j]+S[k]){
37                         dp[i][j+V[k]]=dp[i-1][j]+S[k];
38                         tag[i][j+V[k]]=k;
39                     }
40         int j;
41         for(j=0;j<=start;j++)
42             if(dp[m][start-j]!=-1||dp[m][start+j]!=-1) break;
43         int sum=dp[m][start-j]>dp[m][start+j]?start-j:start+j;
44         int P=(dp[m][sum]+sum-start)/2,D=(dp[m][sum]-sum+start)/2;
45         printf("Jury #%d
",++num);
46         printf("Best jury has value %d for prosecution and value %d for defence:
",P,D);
47         int res[25];
48         for(rg int i=0,j=m,k=sum;i<m;i++){
49             res[i]=tag[j][k];
50             k-=V[tag[j][k]];
51             j--;
52         }
53         sort(res,res+m);
54         go(i,0,m-1) printf(" %d",res[i]);
55         puts("");puts("");
56     }
57     return 0;
58 }
代码戳这里
原文地址:https://www.cnblogs.com/THWZF/p/10940433.html