【poj1015】Jury Compromise

245 篇文章 0 订阅

Description

In Frobnia, a far-away country, the verdicts in court trials are determined by a jury consisting of members of the general public. Every time a trial is set to begin, a jury has to be selected, which is done as follows. First, several people are drawn randomly from the public. For each person in this pool, defence and prosecution assign a grade from 0 to 20 indicating their preference for this person. 0 means total dislike, 20 on the other hand means that this person is considered ideally suited for the jury. 
Based on the grades of the two parties, the judge selects the jury. In order to ensure a fair trial, the tendencies of the jury to favour either defence or prosecution should be as balanced as possible. The jury therefore has to be chosen in a way that is satisfactory to both parties. 
We will now make this more precise: given a pool of n potential jurors and two values di (the defence's value) and pi (the prosecution's value) for each potential juror i, you are to select a jury of m persons. If J is a subset of {1,..., n} with m elements, then D(J ) = sum(dk) k belong to J 
and P(J) = sum(pk) k belong to J are the total values of this jury for defence and prosecution. 
For an optimal jury J , the value |D(J) - P(J)| must be minimal. If there are several jurys with minimal |D(J) - P(J)|, one which maximizes D(J) + P(J) should be selected since the jury should be as ideal as possible for both parties. 
You are to write a program that implements this jury selection process and chooses an optimal jury given a set of candidates.

Input

The input file contains several jury selection rounds. Each round starts with a line containing two integers n and m. n is the number of candidates and m the number of jury members. 
These values will satisfy 1<=n<=200, 1<=m<=20 and of course m<=n. The following n lines contain the two integers pi and di for i = 1,...,n. A blank line separates each round from the next. 
The file ends with a round that has n = m = 0.

Output

For each round output a line containing the number of the jury selection round ('Jury #1', 'Jury #2', etc.). 
On the next line print the values D(J ) and P (J ) of your jury as shown below and on another line print the numbers of the m chosen candidates in ascending order. Output a blank before each individual candidate number. 
Output an empty line after each test case.

Sample Input

4 2 
1 2 
2 3 
4 1 
6 2 
0 0 

Sample Output

Jury #1 
Best jury has value 6 for prosecution and value 4 for defence: 
 2 3 

Hint

If your solution is based on an inefficient algorithm, it may not execute in the allotted time.

Source

中文题干:

描述
在遥远的国家佛罗布尼亚,嫌犯是否有罪,须由陪审团决定。陪审团是由法官从公众中挑选的。先随机挑选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 

我觉得做到这道题适合来畅谈一下了。

首先,我要提供一种错误的思路,是我今天WA了一下午但是依然百思不得其解的思路。

如果您急于求成,可以直接跳过;如果你闲得无聊,请继续:

假如我们设f[i][j]为前i个人选出了j个人的差的最小值,那么我们只需要从f[i-1][j]和f[i-1][j-1]考虑转移就行了,分别是选当前和不选当前的情况。然后最后输出f[n][m],问题就迎刃而解了。这不是很合理吗?

再次声明,这个思路是错误的,因为这里存在一个盲区:

本道题的题意非常明确,简单说就是:把选的m个人的所有的控方分数都加起来,再把所有的辩方分数都加起来,再相减,让绝对值最小,同时和最大。可以感受一下,存在这样一种情况,就是有一组数据,在这个范围内没有被选到,而在下一个范围内却被选到了。其实这就打破的dp的原则:最优子结构无后效性

所以结论是,这样的dp是无法建立的。

那么怎么让它没有后效性呢?通常的办法是加维。本题加维的方法应该也是可以实现的。但是我们不妨换一种思路:可不可以考虑其他的状态转移?

下面是正确的解法,来自,各种神犇的题解:

其实这种转移方法和加维的性质是差不多的,都是通过控制差值取消了后效性。设p[i][k]为记录当选了 i 个人辩控差是k的时候辩控和最大值path[i][k]为记录当选了i个人辩控差是k 然后对应dp数组值<辩控和最大>的前一个人的编号

还需要一个修正值:fix。这个是防止出现负数下标的。相减的时候可能会出现负数。

其实这道题理解了状态的表示就好说了。大神们看到这一步估计就已经开始匆匆写代码了。如果不会处理细节问题的话,可以好好读读代码。

【代码】

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
struct hp{
    int p,d,s,v;
}pre[210];
int path[25][810],dp[25][810]; 
int id[25],n,m,T;
bool select(int j,int k,int i){
    while(j>0&&path[j][k]!=i){
        k-=pre[path[j][k]].v;
        j--;
    }
    if(j==0) return true;
    else return false;
}
int main(){
    while(~scanf("%d%d",&n,&m))
    {
        if(!n&&!m) return 0;
        memset(path,0,sizeof(path));
        memset(pre,0,sizeof(pre));
        memset(dp,-1,sizeof(dp));
        for(int i=1;i<=n;++i){
            scanf("%d%d",&pre[i].p,&pre[i].d);
            pre[i].s=pre[i].p+pre[i].d;
            pre[i].v=pre[i].p-pre[i].d;
        }
        int fix=m*20;  
        dp[0][fix]=0;
        for (int i=1;i<=m;++i)
          for (int k=0;k<=fix*2;++k)
            if (dp[i-1][k]>=0)
                for (int j=1;j<=n;++j)
                  if (dp[i-1][k]+pre[j].s>dp[i][k+pre[j].v]&&select(i-1,k,j)){
                    dp[i][k+pre[j].v]=dp[i-1][k]+pre[j].s;
                    path[i][k+pre[j].v]=j;
                  }
        int k;
        for(k=0;k<=fix;++k)
          if(dp[m][fix-k] >= 0 || dp[m][fix+k] >= 0) break;
        int div=dp[m][fix-k]>dp[m][fix+k]?(fix-k):(fix+k); 
        int D = (dp[m][div] - div + fix)/2;
        int P = (dp[m][div] + div - fix)/2;
        printf("Jury #%d\n", ++T);
        printf("Best jury has value %d for prosecution and value %d for defence:\n", P, D);
        for(int i=0,j=m,k=div;i<m;++i){
            id[i] = path[j][k];
            k -= pre[id[i]].v;
            j--;
        }
        sort(id,id+m);
        for(int i=0;i<m;++i)
        printf(" %d", id[i]);
        printf(" \n\n");
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值