题目大意: 在遥远的国家佛罗布尼亚,嫌犯是否有罪,须由陪审团决定。陪审团是由法官从公众中挑选的。先随机挑选n 个人作为陪审团的候选人,然后再从这n 个人中选m 人组成陪审团。选m 人的办法是:控方和辩方会根据对候选人的喜欢程度,给所有候选人打分,分值从0 到20。为了公平起见,法官选出陪审团的原则是:选出的m 个人,必须满足辩方总分和控方总分的差的绝对值最小。如果有多种选择方案的辩方总分和控方总分的之差的绝对值相同,那么选辩控双方总分之和最大的方案即可。最终选出的方案称为陪审团方案。(d[k]为控方对每个人的评分,p[k]为辩方对每个人的评分)
解题思路:1)第一感觉就是个背包,n个人每一个都有取和不取的两种情况。
2)设状态:对于每一个可行的方案,有三个量,一个是陪审团的人数,一个是差值,一个是和。很明显,在题目中,我们可以知道陪审团的人数是有范围的,而且差值是有范围(-400,400)
我们不妨设dp[i][j]表示由i个人组成的陪审团,差值为j的方案的总分最大和。
3)状态的转移:其实可以发现,对于每一个方案dp[i][j],都可以由方案dp[i-1][j+d[k]-p[k]]+d[k]+p[k](1<=k<=n)变换而来。
4)这里j表示的是差值,由于差值的范围是-400到400,由于C++中坐标不能为负数,所以要加上400变为0到800即可以处理这个问题。
代码:
1#include <iostream>
2#include <cstdio>
3#include <cstring>
4#include <cmath>
5#include <string>
6#include <algorithm>
7
8using namespace std;
9
10struct player
11{
12 int d,p;
13}num[300]; //表示控方和辩方分别给的分数
14int result[300]; //储存最终的m个陪审团成员
15int n,m,x,y,xx,yy;
16int path[30][1000]; //记录选择i个人,以差值j结尾的陪审团成员的最后一个人
17int dp[30][1000]; //记录选择i个人,差值为j的最大和是多少
18
19void init()
20{
21 memset(path,0,sizeof(path));
22 memset(result,0,sizeof(result));
23 for (int i=0; i<=m; i++)
24 for (int j=0; j<=800; j++)
25 {
26 dp[i][j]=-1;
27 }
28 dp[0][400]=0; //400是贯穿整个程序的,目的是为了使差值由负变正,可以在数组在中储存
29}
30
31void get_dp()
32{
33 for (int i=0; i<=m-1; i++)
34 {
35 for (int j=0; j<=800; j++)
36 {
37 for (int l=1; l<=n && dp[i][j]>=0; l++)
38 {
39 if (dp[i+1][j+num[l].d-num[l].p]<dp[i][j]+num[l].d+num[l].p)
40 {
41 x=i;
42 y=j;
43 while(x>0&&path[x][y]!=l)
44 {
45 y-=num[path[x][y]].d-num[path[x][y]].p; //判断这个新加入的l在原来的成立的陪审团中是否存在
46 x--;
47 }
48 if(x==0)
49 {
50 dp[i+1][j+num[l].d-num[l].p]=dp[i][j]+num[l].d+num[l].p;
51 path[i+1][j+num[l].d-num[l].p]=l;
52 }
53 }
54 }
55 }
56 }
57}
58
59int cmp(int a,int b)
60{
61 return a<b;
62}
63
64int main()
65{
66 int T=0;
67 while (~scanf("%d%d",&n,&m))
68 {
69 T++;
70 if (n==0 && m==0) break;
71 init(); //初始化
72 int xx,yy;
73 for (int i=1; i<=n; i++)
74 {
75 scanf("%d%d",&xx,&yy);
76 num[i].d=xx;
77 num[i].p=yy;
78 }
79 get_dp(); //dp[i][j]表示的是选择i个人,差值为j的最大和是多少
80 int k=0;
81 int i=400; //400是贯穿整个程序的,目的是为了使差值由负变正,可以在数组在中储存
82 int temp;
83 while (dp[m][i+k]<0 && dp[m][i-k]<0) k++; //求出最小的差值
84
85
86 //找出m个人的陪审团成员,和统计m个人的陪审团控方得分和和辩方得分和
87 if (dp[m][i+k]>dp[m][i-k]) temp=i+k;
88 else temp=i-k;
89 int x=y=0;
90 for(int i=0; i<=m-1; i++)
91 {
92 result[i]=path[m-i][temp];
93 x+=num[result[i]].d;
94 y+=num[result[i]].p;
95 temp-=num[result[i]].d-num[result[i]].p;
96 }
97
98 //题目要求从小到大输出~第一次就WA在这里,注意审题
99 sort(result,result+m,cmp);
100
101 cout << "Jury #" << T << endl;
102 cout << "Best jury has value " << x << " for prosecution and value " << y << " for defence:" << endl;
103 for (int i=0; i<=m-1; i++)
104 cout << " " << result[i];
105 cout << endl;
106 }
107 return 0;
108}
109