NOI题库1980 陪审团的人选(POJ1015)

1980: 陪审团的人选

总时间限制:

1000ms

内存限制:

65536kB

描述

在遥远的国家佛罗布尼亚,嫌犯是否有罪,须由陪审团决定。陪审团是由法官从公众中挑选的。先随机挑选n个人作为陪审团的候选人,然后再从这n个人中选m人组成陪审团。选m人的办法是:

控 方和辩方会根据对候选人的喜欢程度,给所有候选人打分,分值从0到20。为了公平起见,法官选出陪审团的原则是:选出的m个人,必须满足辩方总分和控方总 分的差的绝对值最小。如果有多种选择方案的辩方总分和控方总分的之差的绝对值相同,那么选辩控双方总分之和最大的方案即可。

输入

输入包含多组数据。每组数据的第一行是两个整数n和m,n是候选人数目,m是陪审团人数。注意,1<=n<=200, 1<=m<=20 而且 m<=n。接下来的n行,每行表示一个候选人的信息,它包含2个整数,先后是控方和辩方对该候选人的打分。候选人按出现的先后从1开始编号。两组有 效数据之间以空行分隔。最后一组数据n=m=0

输出

对每组数据,先输出一行,表示答案所属的组号,如 'Jury #1', 'Jury #2', 等。接下来的一行要象例子那样输出陪审团的控方总分和辩方总分。再下来一行要以升序输出陪审团里每个成员的编号,两个成员编号之间用空格分隔。每组输出数据须以一个空行结束。

样例输入

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

来源

Southwestern European Regional Contest 1996, POJ 1015, 程序设计实习2007

 

【思路】

  DP。

  设d[i][j]表示该选第i个人且辩方与控方之差为j时最大的辩控和。

  设P为辩方分数D为控方分数,v(i)=P[i]-D[i],S(i)=P[i]+D[i]

  有如下转移式:

     d[i][j]=max{ d[i-1][j-v(k)]+S(k) }

  转移式表示第i个人选k,且k必须要满足在d[i-1][j-v(k)]的最优选择中没有出现过。

  用path[i][j]记录差值为j时所选的第i个人,一方面检查k是否出现过,一方面方便构造解。

  差值会为负值因此将差值全部偏移N个单位。

【代码】

 

 1 #include<iostream>
 2 #include<cstring>
 3 #include<vector>
 4 #include<cstdio>
 5 #include<algorithm>
 6 using namespace std;
 7 
 8 const  int maxn = 400+10;
 9 
10 int d[25][5*maxn];
11 int path[25][5*maxn];
12 int P[maxn],D[maxn];
13 
14 int n,m;
15 vector<int> ans;
16 
17 int main() {
18     ios::sync_with_stdio(false);
19     int kase=0;
20     while(cin>>n>>m && (n&&m)) {
21         for(int i=1;i<=n;i++) cin>>P[i]>>D[i];
22         memset(d,-1,sizeof(d));
23         memset(path,0,sizeof(path));
24         int N=m*20;
25         d[0][N]=0;
26         for(int i=0;i<m;i++) {
27             for(int j=0;j<=2*N;j++) if(d[i][j]>=0)
28                {
29                     for(int k=1;k<=n;k++) {
30                         if(d[i][j]+P[k]+D[k]>d[i+1][j+P[k]-D[k]]) {
31                             int ti=i,tj=j;
32                             while(ti>0 && path[ti][tj]!=k) {
33                                 tj-=P[path[ti][tj]]-D[path[ti][tj]];
34                                 ti--;
35                             }
36                             if(!ti) {
37                                 d[i+1][j+P[k]-D[k]]=d[i][j]+P[k]+D[k];
38                                 path[i+1][j+P[k]-D[k]]=k;
39                             }
40                         }
41                     }
42                }
43         }
44         int i=N,j=0,k,totP=0,totD=0;
45         while(d[m][i+j]<0&&d[m][i-j]<0)  j++;
46         if(d[m][i+j]>d[m][i-j])  k=i+j;
47         else k=i-j;
48         ans.clear();
49         for(i=1;i<=m;i++)
50         {
51             ans.push_back(path[m-i+1][k]) ;
52             k-=P[ans[i-1]]-D[ans[i-1]];
53             totP += P[ans[i-1]]; totD += D[ans[i-1]];
54         }
55         sort(ans.begin(),ans.end());
56         printf("Jury #%d\n",++kase);
57         printf("Best jury has value %d for prosecution and value %d for defence:\n",totP,totD);
58         for(i=0;i<m;i++)  
59           printf(" %d",ans[i]);
60         printf("\n\n");   
61     }
62     return 0;
63 }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值