poj 1015 Jury Compromise

Jury Compromise
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 23006 Accepted: 5949 Special Judge

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. 

题意:有n的候选人,从中选出m个人组成陪审团。每个人给出一个p(控值)和一个d(辩值),要使得选出的  |m个人的p值总和-m个人的d值总和|  (即控辩差的绝对值)最小,若有最小的情况有多种,则选取p值总和+d值总和最大一种的情况,并输出这种情况的p值总和与d值总和,并输出这种情况所选的m个候选人编号。

思路:
设dp【j】【k】表示选了j个人,控辩差为k的最大和,题目说明了每个人的p值和d值取值范围都是0--20,显然k的取值范围是-m*20-m*20,因为数组没有负数的下标,所以要将k值加上m*20。dp【】【】最初赋值为-1,dp【j】【k】>=0表示选了j个人,控辩差为k的情况存在,从而选下一个人时可以以dp【j】【k】为依据来选。当然下一个人不能是前面已选的,所以要设数组path【j】【k】保存选了j个人,控辩差为k的j个人中最后一个人的编号。

AC代码:
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <vector>
#include <cstdlib>

using namespace std;
const int INF=100000000;

int dp[30][1000];    //dp[j][k]表示选出j个人,控辩差为k时的最大控辩和
int path[30][1000];  //path[j][k]表示选出j个人,控辩差为k时的陪审团成员的最后一人
int p[205],d[205];
int ans[30];        //保存最终选出的陪审员
int main()
{
    int n,m,t=0;
    while(cin>>n>>m)
    {
        if(n==0&&m==0)
        break;
        t++;
        memset(dp,-1,sizeof(dp));
        memset(path,0,sizeof(path));
        for(int i=1;i<=n;i++)
        cin>>p[i]>>d[i];
        int p_d=m*20;          //使差值由负变正,可以在数组中储存
        dp[0][p_d]=0;
        for(int j=0;j<m;j++)
        for(int k=0;k<=p_d*2;k++)
        if(dp[j][k]>=0)          //表示存在选出j个人,控辩差为k的情况
        {
            for(int i=1;i<=n;i++)
            if(dp[j+1][k+p[i]-d[i]]<dp[j][k]+p[i]+d[i])
            {
                int t1=j,t2=k;                  //判断新加入的i是否在之前已选的人中存在
                while(t1>0&&path[t1][t2]!=i)     //从最后一人倒推到第一人
                {
                    t2-=p[path[t1][t2]]-d[path[t1][t2]];
                    t1--;
                }
                if(t1==0)
                {
                    dp[j+1][k+p[i]-d[i]]=dp[j][k]+p[i]+d[i];
                    path[j+1][k+p[i]-d[i]]=i;
                }
            }
        }
        int i=p_d;
        int k,j=0;
        while(dp[m][i+j]<0&&dp[m][i-j]<0) j++;   //找出控辩差(绝对值)最小的最大和
        if(dp[m][i+j]>dp[m][i-j]) k=i+j;         //若有两种情况(一负一正),找最大和较大的那个情况的差值
        else k=i-j;

        cout<<"Jury #"<<t<<endl;
        //设p的总和为x,d的总和为y,有方程组{x-y+p_d=k;  x+y=dp[m][k]}
        cout<<"Best jury has value "<<(k-p_d+dp[m][k])/2<<" for prosecution and value "<<(dp[m][k]-k+p_d)/2<<" for defence:"<<endl;
        for(int i=1;i<=m;i++)   //还原路径,保存最终选出来的陪审员
        {
        ans[i]=path[m-i+1][k];
        k-=p[ans[i]]-d[ans[i]];
        }
        sort(ans+1,ans+m+1);
        for(int i=1;i<=m;i++)
        cout<<" "<<ans[i];
        cout<<endl<<endl;
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值