POJ - 1015 Jury Compromise

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.

题意:给定n个 辩方和控方的值,要求选出m个,使得这m个值的辩方值减去控方值最小,如果有多个最小值相同的方案,则输出

辩方值加上控方值最大的一组,并按顺序打印编号。

样例解释:

4 2 

 1 2   -1

2 3   -1

4 1   3

6 2   4

4组人,选出2组,最小值值为2,并且1+2,和2+3都为2,所以这时候需要选择和最大的,选择2+3;

思路 :DP,dp[i][k] 表示,选i个人,他们的差值为k时的最大和。最后我们只需要看dp[m][x]即可,这里解释一下我们为什么要这样选择状态表示,题目最后需要输出我们选择的辩方值总和,控方值总和,我们发现如果我们知道两者之和和两者之差,那么我们可以通过相加除2和相减除2,分别求出他们。

dp[i][k]=min(dp[i-1][x])+s[i](x=k-v[i],x>=0&&x<k)

当然,题目最难的部分不是写出状态方程,而是处理状态方程,首先,k可能为负数,因此需要整体加上一个大的正数,还需要用path数组来记录当前选取了那个人的编号。

下面给出代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
#include <map>
#include <vector>
#include <set>
#include <string>
#include <math.h>
#include <stack>
using namespace std;
const int maxn = 1e4+100;
#define inf 1e9
int dp[30][maxn];
int path[30][maxn];
int p[maxn],d[maxn],s[maxn],v[maxn];
int n,m;
bool judge(int j,int k,int i,int* v)
{
	while(j>0 && path[j][k]!=i)
	{
		k-=v[ path[j][k] ];
		j--;
	}
	return j?false:true;
}
int main(int argc, char const *argv[])
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    #endif
    int Case=0;
    while(scanf("%d%d",&n,&m)!=EOF&&n+m)
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&p[i],&d[i]);
            s[i]=p[i]+d[i];
            v[i]=p[i]-d[i];
        }
        memset(dp,-1,sizeof(dp));
        memset(path,0,sizeof(path));
        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][k+v[j]]<dp[i-1][k]+s[j])
                        {
                            if(judge(i-1,k,j,v))//需要检查这个j是否在之前被选过,因为只能选一次
                            {
                                dp[i][k+v[j]]=dp[i-1][k]+s[j];
                                path[i][k+v[j]]=j;
                            }
                        }
                    }
                }
            }
        }
        int k;
        for(k=0;k<=fix;k++)//dp结束后需要去搜索正确答案,从0开始的第一个满足的值,就是最小值
        {
            if(dp[m][fix-k]>=0||dp[m][fix+k]>=0)
            {
                break;
            }
        }
        int ans=dp[m][fix-k]>dp[m][fix+k]?fix-k:fix+k;
        printf("Jury #%d\n",++Case);
        int ans1=(dp[m][ans]+ans-fix)/2;//等价于,已知a+b,a-b,求a,b;
        int ans2=(dp[m][ans]-ans+fix)/2;//
        printf("Best jury has value %d for prosecution and value %d for defence:\n",ans1,ans2);
        int fans[maxn];
        int i=m,cnt=0;
        while(m>0&&path[m][ans])
        {
            fans[cnt++]=path[m][ans];
            ans-=v[ path[m][ans] ];
            m--;
        }
        sort(fans,fans+cnt);
        for(int j=0;j<cnt-1;j++)
        {
            printf("%d ",fans[j]);
        }
        printf("%d\n",fans[cnt-1]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40774175/article/details/81225598