POJ 1015 Jury Compromise

题目大意:有n个人,每个人都有两种评价,第i个人的评价分别为d[i],p[i]。然后给你一个m,让你从中选出m个人,使方案最优,最优方案定义为:令选出m个人的集合为M,则(1)| ∑d[i] - ∑p[i] | 最小,i∈M;(2)在| ∑d[i] - ∑p[i] | 相同的多种方案中,选∑d[i] +∑p[i] 最大的,i∈M。最后输出∑d[i] 和∑p[i]的值,以及被选上人的编号。

思路:这道题是道DP题,但我始终没有想出递推公式,因为我没有想到把| ∑d[i] - ∑p[i] |作为递推参数。由于题目给出等级范围为0~20,因此m最大为20的情况下, ∑d[i] - ∑p[i]  的范围为[-400,400],这就为DP提供了可能。

(1)设置dp(i,j),含义为选出i个人,∑d[i] - ∑p[i] 为j的情况下,∑d[i] +∑p[i] 最大值。则当前dp(i-1,j)存在有效值时,状态转移公式为:dp(i,j+d[x]-p[x])= max(dp(i,j+d[x]-p[x]),dp[i-1]+d[x]+p[x]),max里前一个参数代表不选x,后一个代表选x。其中x满足在dp(i-1,j)方案中,x没有被选过。

(2)设置path(i,j),含义为选出i个人,∑d[i] - ∑p[i] 为j的情况下,最后一个被选上的人的编号,则倒数第二个被选上的人的编号为path(i-1,j - (d[path(i,j)]-p[path(i,j)])),依次类推,则能找到dp(i,j)方案前i个人的所有编号。

(3)由于数组下标不允许出现负数,所以将dp和path的第二个参数都加上20*m,再进行处理。

 

#include <iostream>
#include <algorithm>

using namespace std;

int n,m,p[205],d[205];
int dp[25][805],path[25][805],ans[25];

bool test(int j,int k,int i)
{
    while (j>0 && path[j][k]!=i)
    {
        k-=(p[path[j][k]]-d[path[j][k]]);
        j--;
    }
    if (j==0)
        return true;
    return false;
}

int main()
{
    int i,j,k,test_cases=1;
    while (scanf("%d%d",&n,&m)==2 && (n||m))
    {
        for (i=1;i<=n;i++)
            scanf("%d%d",&p[i],&d[i]);
        memset(dp,-1,sizeof(dp));
        memset(path,0,sizeof(path));
        dp[0][m*20]=0;
        for (j=1;j<=m;j++)
            for (k=0;k<=m*40;k++)
                if (dp[j-1][k]!=-1)
                    for (i=1;i<=n;i++)
                        if (dp[j][k+p[i]-d[i]]<dp[j-1][k]+p[i]+d[i] && test(j-1,k,i))
                        {
                            dp[j][k+p[i]-d[i]]=dp[j-1][k]+p[i]+d[i];
                            path[j][k+p[i]-d[i]]=i;
                        }
        for (j=0;dp[m][m*20+j]==-1 && dp[m][m*20-j]==-1;j++);
        if (dp[m][m*20+j]>dp[m][m*20-j])
            k=m*20+j;
        else
            k=m*20-j;
        printf("Jury #%d\n",test_cases++);
        printf("Best jury has value %d for prosecution and value %d for defence:\n",(dp[m][k]+k-m*20)/2,(dp[m][k]-k+m*20)/2);
        for (i=0;i<m;i++)
        {
            ans[i]=path[m-i][k];
            k-=(p[path[m-i][k]]-d[path[m-i][k]]);
        }
        sort(ans,ans+m);
        for (i=0;i<m;i++)
            printf(" %d",ans[i]);
        printf("\n\n");
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值