poj1015

题目描述:有n个人,需要从中选出m个人,每个人都有一个d和一个p值,要求选出来的m个人满足:在所有的方案中,所有人的p值之和减去所有人的d值之和的绝对值最小,如果最小的绝对值相等,则取p值之和加上d值之和最大的方案。
输入格式:有多组数据,每组数据n+1行,第一行两个整数n和m,接下来n行每行两个整数,表示每个人的d值和p值,以0 0结束输入。
输出格式:每组数据输出三行,第一行表示当前数据组数的字符串(“Jury #1”,”Jury #2”, etc.),第二行输出表示该方案的所有人的d值之和与p值之和的字符串(“Best jury has value P for prosecution and value D for defence:”),第三行输出该方案选出来的人的编号,每个人的编号之前都有一个空格。每组数据输出完之后输出一行空格。
数据约定:0 < n <= 200,0< m <= 20,0 <= p,q <= 20。
解题思路:DP!!!
状态:f[i][j]表示已经选出来了i个人并且差值为j时的最大和值。
转移:f[i+1][j+p[k]-d[k]] = max{f[i][j]+p[k]+d[k]},第一重循环i从1开始枚举,第二重循环j从-400枚举到+400(具体实现需要把j整体移400,C数组下标从0开始的),第三重循环k从1枚举到n。。。
一开始我以为是搜索。。妈蛋的谁叫前面的两道题都是搜索。。后来发现是DP,然后想了想状态和转移,发现还要记录路径,天坑。。。然后我就可耻的看了别人的题解。发现其实dp的时候带上路径并没有想象中的那么难。。但是不知道为什么讨论区好多人说标准的dp解法有错误。。不懂。。。。
我的代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAX_N 222

int main(int argc, char const *argv[])
{
    int m, n, cnt=0, p[MAX_N], d[MAX_N];
    int ans[MAX_N][MAX_N*4], path[MAX_N][MAX_N*4];
    while(scanf("%d%d", &n, &m) && n && m){
        //preprocessing and get input
        memset(ans,-1,sizeof(ans));
        memset(path,-1,sizeof(path));
        int zero=m*20; ans[0][zero]=0;
        for(int i=0; i<n; ++i){ scanf("%d%d", p+i, d+i); }

        // dynamic programming
        for(int i=1; i<=m; ++i){
            for(int j=0; j<=zero*2; ++j){
                if(ans[i-1][j]==-1) { continue; }
                for(int k=0; k<n; ++k){
                    int nums=i-1, value=j, flag=false;
                    while(nums>0){ 
                        int prev=path[nums][value];
                        --nums && (value-=p[prev]-d[prev]);
                        if(prev==k) { flag=true; break; }
                    }
                    if(flag) { continue; }
                    if(ans[i-1][j]+d[k]+p[k]>ans[i][j+p[k]-d[k]]){
                        ans[i][j+p[k]-d[k]]=ans[i-1][j]+d[k]+p[k];
                        path[i][j+p[k]-d[k]]=k;
                    }
                }
            }
        }

        //output the answer
        int i=zero, j=0, k=zero, err, answer[MAX_N];
        while(ans[m][i+j]<0 && ans[m][i-j]<0 && ++j);
        err=(k=ans[m][i+j]>ans[m][i-j]?i+j:i-j)-zero;
        printf("Jury #%d\n",++cnt);
        printf("Best jury has value %d for prosecution and value %d for defence:\n",(err+ans[m][k])/2,(ans[m][k]-err)/2);
        for(int i=0; i<m; ++i){
            answer[i]=path[m-i][k];
            k-=p[answer[i]]-d[answer[i]];
        }
        std::sort(answer,answer+m);
        for(int i=0; i<m; printf(" %d", answer[i++]+1));
        printf("\n\n");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值