背包求具体方案(20-20)

29 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 20 天,点击查看活动详情

背包求方案数

题目描述

有 NN 件物品和一个容量是 VV 的背包。每件物品只能使用一次。

第 ii 件物品的体积是 viv_i,价值是 wiw_i

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。

输出 字典序最小的方案。这里的字典序是指:所选物品的编号所构成的序列。物品的编号范围是 1N1…N

输入格式

第一行两个整数,NVN,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 NN 行,每行两个整数 vi,wiv_i,w_i,用空格隔开,分别表示第 ii 件物品的体积和价值。

输出格式

输出一行,包含若干个用空格隔开的整数,表示最优解中所选物品的编号序列,且该编号序列的字典序最小。

物品编号范围是 1N1…N

数据范围

0<N,V10000<N,V≤1000
0<vi,wi10000<vi,wi≤1000

输入样例

4 5
1 2
2 4
3 4
4 6

输出样例:

1 4

题目分析

f[i][j]f[i][j] 表示从第 ii 个元素到最后一个元素,总容量为 jj 的价值最大值。

对于第 ii 个元素,我们同样可以以选或不选两种状态来考虑。转移方程为:

f[i][j]=max(f[i+1][j],f[i+1][jv[i]]+w[i])f[i][j]=max(f[i+1][j],f[i+1][j-v[i]]+w[i])

对于字典序最优解,前提条件我们知道 f[1][m]f[1][m] 是价值最大值。

首先对于第一个元素:

f[1][m]=f[2][mv[1]]+w[1]f[1][m]=f[2][m-v[1]]+w[1],可知需要第一个元素。

f[1][m]=f[2][m]f[2][mv[1]]+w[1]f[1][m]=f[2][m]\ne f[2][m-v[1]]+w[1],可知不需要第一个元素。

f[1][m]=f[2][m]=f[2][mv[1]]+w[1]f[1][m]=f[2][m]=f[2][m-v[1]]+w[1],可知第一个元素可需要可不需要,但由于字典序最小的限制,需要选择第一个元素。

依次递归可得答案。

Accept代码

#include <iostream>

using namespace std;

const int N = 1010;

int n, m;
int v[N], w[N];
int f[N][N];

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];

    for (int i = n; i >= 1; i -- )
        for (int j = 0; j <= m; j ++ )
        {
            f[i][j] = f[i + 1][j];    // 此步不可省略
            if (j >= v[i]) f[i][j] = max(f[i][j], f[i + 1][j - v[i]] + w[i]);
        }

    int j = m;
    for (int i = 1; i <= n; i ++ )
        if (j >= v[i] && f[i][j] == f[i + 1][j - v[i]] + w[i])
        {
            cout << i << ' ';
            j -= v[i];
        }

    return 0;
}