poj 1015 Jury Compromise 动态规划

Jury Compromise
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 13507 Accepted: 3452 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.

Source

又是动态规划,这个题自己开始没想出来,几天后看了网上的报告,写的代码,花了很久,大概近4个小时才完成。
这个dp还是二维的。dp[i][j],i表示一共选择几个人,j表示选择i个人时的辩控差。注意实际情况是这个差可能为负,所以作为下标时,每人的辩控差人为的加了20,使其为正,即辩控差最小时(实际是-20)为0。
此题递推关系的建立比较复杂。由于题中要求考虑到辩控差相等时,最优解要取辩控和最大的情况。所以dp[i][j]保存的是当选择i个人使辩控差为j时,辩控和的最大值,即代表了辩控和最大时的情况,也就是小规模的一个最优解(辩控差为j时)。当dp[i][j]已知,对于任意在dp[i][j]中未被选中的人k,有一个递推关系,即dp[i+1][j+v[k]]=dp[i][j]+sum[k](v[k]为第k个人的辩控差,sum[k]为第k个人的辩控和),要注意的是由于dp保存的是每种情况的最大值,所以对于dp[i+1][x](x确定),所有dp[i][j](j可变)与k(k随j 不同而变,每个j有多个k匹配)的组合中,只有一组符合条件。另外为了确定路径,要记录下每个dp[i+1][x]从各个dp[i][j]演变过来时选择的人的号码。这样,就自然而然的需要的一个数组path[i][j]存放由dp[i-1][j-x]递推到dp[i][j]选择的人。这样通过向上搜索,就可以知道从初始化状态到dp[i][j]经历了那些选择,即都选择了几号人。例如到达dp[i][j]的选择是path[i][j],前一个选择是path[i-1][j-v[path[i][j]],(path的各维意义与dp数组相同)。此题的一个很大的技巧也就在于path数组的构建,它利用dp相同的维度,记录每个递推时作出选择或运算的信息,最后构建最优解。
代码:
 #include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
int sum[210],v[210];
int dp[210][810],path[210][810];
bool issel(int num,int v,int ser);
int a[201][3];
int result[21];
int main()
{
    int n,m,times=0;
    while(scanf("%d%d",&n,&m),n||m)
    {
        for(int i=0;i<m+1;++i)
        for(int j=0;j<810;++j)
        dp[i][j]=-1;
        for(int i=1;i<n+1;++i)
        {
            scanf("%d%d",&a[i][1],&a[i][2]);
            sum[i]=a[i][1]+a[i][2];
            v[i]=a[i][1]-a[i][2]+20;
        }
        dp[0][0]=0;
        for(int i=0;i<m;++i)
            for(int j=0;j<801;++j)
            {
                if(dp[i][j]==-1)
                {
                    //cout<<"i="<<i<<"j="<<j<<"= "<<dp[i][j]<<"    ";
                    continue;
                }
                for(int k=1;k<n+1;++k)
                {
                    if(issel(i,j,k))
                    continue;
                    if(dp[i+1][j+v[k]]<sum[k]+dp[i][j])
                    {
                        path[i+1][j+v[k]]=k;
                        dp[i+1][j+v[k]]=dp[i][j]+sum[k];
                        //cout<<"aaaaaaaaaa"<<endl;
                        //cout<<"dp"<<i+1<<" "<<j+v[k]<<" "<<dp[i+1][j+v[k]]<<endl;
                    }
                }
            }
        int minv=-1,minvv=10000,o=20*m,maxsum=0;
        for(int i=0;i<801;++i)
        {
            if(dp[m][i]!=-1)
            {
                //cout<<"a";
                if(fabs((double)o-i)<minvv)
                {
                    minvv=fabs((double)o-i);
                    minv=i;
                    maxsum=dp[m][i];
                }
                else if(fabs((double)o-i)==minvv&&maxsum<dp[m][i])
                {
                    minv=i;
                    maxsum=dp[m][i];
                }
            }
        }
        //cout<<"choose "<<minv<<endl;
        int md=0,mp=0;
        for(int i=m,j=minv;i>=1;--i)
        {
            result[i]=path[i][j];
            j-=v[path[i][j]];
        }
        sort(result+1,result+m+1);
        for(int i=1;i<m+1;++i)
        {
            md+=a[result[i]][1];
            mp+=a[result[i]][2];
        }
        cout<<"Jury #"<<++times<<endl;
        printf("Best jury has value %d for prosecution and value %d for defence:/n",md,mp);
        for(int i=1;i<m+1;++i)
        cout<<" "<<result[i];
        cout<<endl;
        //for(int i=0;i<801;++i)//
        //if(dp[m][i]!=-1)
        //cout<<i<<": "<<dp[m][i]<<" ";/
    }
   
    system("PAUSE");
    return 0;
}
bool issel(int num,int vt,int ser)
{
    int i=num,j=vt;
    for(;i>=0;--i)
    {
        if(path[i][j]==ser)
        return true;
        j-=v[path[i][j]];
    }
    return false;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值