poj1015 01背包+输出路径

本文深入解析了一道经典的动态规划(DP)算法竞赛题目,详细介绍了如何通过DP算法解决复杂的选择问题,以达到|sigma(a)-sigma(b)|最小且sigma(a)+sigma(b)最大的目标。文章提供了完整的代码实现,包括状态定义、状态转移方程和路径更新策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给定人数n(1<=n<=200),每个人有一个a, b。(0<=a<=b),从中选择m个人,使得|sigma(a)-sigma(b)|最小,若相等则sigma(a)+sigma(b)最大。求最优策略的sigma(a),sigma(b)和选择的人的编号

dp[i][j]:选择了i个人,|sigma(a)-sigma(b)|==j时sigma(a)+sigma(b)最大为dp[i][j]。
由于|sigma(a)-sigma(b)|可能有负值,且最大负值为-20*m,所以令0 -> -20*m, 20*m->0, 2*20*m->20*m。
在状态转移的时候更新路径即可。

#include <cstdio>
#include <vector>
#include <cstring>

using namespace std;
const int N = 210;
const int INF = 0x3f3f3f3f;
int dp[N][N<<2];
vector <int> path[N][N<<2];
int add[N],sub[N];
int n, m;

int main()
{
    int cas = 0;
    while(~scanf("%d %d", &n, &m) && (m+n))
    {
        for(int i = 1; i <= m; ++i)
            for(int j = 0; j < N; ++j)
                path[i][j].clear();
        for(int i = 1; i <= n; ++i)
        {
            int a, b;
            scanf("%d %d", &a, &b);
            add[i] = a+b;
            sub[i] = a-b;
        }
        memset(dp, -1, sizeof(dp));
        int zero = 20*m;
        dp[0][zero] = 0;
        for(int i = 1; i <= n; ++i)
            for(int j = m-1; j>=0; --j)
                for(int k = 2*zero; k>0 ; --k)
                    if(~dp[j][k] && (k+sub[i]>=0&&k+sub[i]<=2*zero))
                        if(dp[j][k]+add[i] > dp[j+1][k+sub[i]])
                        {
                            dp[j+1][k+sub[i]] = dp[j][k]+add[i];
                            path[j+1][k+sub[i]] = path[j][k];
                            path[j+1][k+sub[i]].push_back(i);
                        }
        int i = 0;
        while(dp[m][i+zero]==-1 && dp[m][-i+zero]==-1) ++i;
        int tmp = dp[m][i+zero]>dp[m][-i+zero]?i+zero:-i+zero;
        int ans1 = (dp[m][tmp]+(tmp-zero))/2;
        int ans2 = (dp[m][tmp]-(tmp-zero))/2;
        printf("Jury #%d\n", ++cas);
        printf("Best jury has value %d for prosecution and value %d for defence:\n", ans1, ans2);
        for(int i = 0; i < m; ++i)
            printf(" %d", path[m][tmp][i]);
        printf("\n\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值