uva 624 CD(01背包+路径打印,背包九讲之输出路径)

更新一下背包九讲关于输出路径的讲法

一般而言,背包问题是要求一个最优值,如果要求输出这个最优值的方 案,可以参照一般动态规划问题输出方案的方法:记录下每个状态的最优值是 由状态转移方程的哪一项推出来的,换句话说,记录下它是由哪一个策略推出 来的。便可根据这条策略找到上一个状态,从上一个状态接着向前推即可。 还是以01背包为例,方程为F[i,v] = max{F[i−1,v],F[i−1,v−Ci]+Wi}。 再用一个数组G[i,v],设G[i,v] = 0表示推出F[i,v]的值时是采用了方程的前一 项(也即F[i,v] = F[i−1,v]),G[i,v] = 1表示采用了方程的后一项。注意这 两项分别表示了两种策略:未选第i个物品及选了第i个物品。那么输出方案的 伪代码可以这样写(设最终状态为F[N,V ]):
i := N
v := V
if G[i,v] = 0 print 未选第i项物品
else if G[i,v] = 1 print 选了第i项物品
v := v−Ci//没选的话不减
i := i−1
另外,采用方程的前一项或后一项也可以在输出方案的过程中根据F[i,v]的值 实时地求出来。也即,不须纪录G数组,将上述代码中的G[i,v] = 0改成F[i,v] = F[i−1,v],G[i,v] = 1改成F[i,v] = F[i−1][v−Ci] + Wi也可。

————————————————————————————————————————————————
题意:一段n分钟的路程,磁带里有m首歌,每首歌有一个时间,求最多能听多少分钟的歌,并求出是拿几首歌。
思路:看似是很简单的01背包,但是加上了路径打印之后死活做不出,可能是我对dp的理解还不够深。

回归正题,01背包的求解就不说了。下面说下路径打印的两种方法。因为需要路径打印所以肯定不能只使用一个滚动数组来求解答案,如果真的一定要使用滚动数组,那么就需要重新定义一个二维数组path来记录状态。同理,如果使用了二维dp数组来记录状态的话,就可以直接进行打印了。(总结一下,dp要想打印路径的话,一定要记录所有的状态,不能使用滚动数组)

方法一:一维dp

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 105;
int n, m, v;
int dp[123131];
int a[maxn];
int path[maxn][123146];
int main() {
    while(~scanf("%d%d", &v, &n)) {
        memset(dp, 0, sizeof(dp));
        memset(path, 0, sizeof(path));
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
        }
        for(int i = 1; i <= n; i++) {
            for(int j = v; j >= a[i]; j--) {
                if(dp[j] < dp[j - a[i]] + a[i]) {
                    dp[j] = dp[j - a[i]] + a[i];
                    path[i][j] = 1;
                }
            }
        }
        for(int i = n, j = v; i >= 1; i--) {
            if(path[i][j]) {
                printf("%d ", a[i]);
                j -= a[i];
            }
        }
        printf("sum:%d\n", dp[v]);
    }
    return 0;
}

方法二:
使用二维dp。(推荐这种方法,节省空间)


#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 105;
int n, m, v;
int dp[maxn][123131];
int a[maxn];
int main() {
    while(~scanf("%d%d", &v, &n)) {
        memset(dp, 0, sizeof(dp));
        memset(path, 0, sizeof(path));
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
        }
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= v; j++) {//注意01背包二维形式的写法
                if(a[i] <= j)  dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - a[i]] + a[i]);
                else dp[i][j] = dp[i - 1][j];
            }
        }

        int temp = v;
        for(int i = n; i >= 1; i--) {
            if(dp[i][temp] == dp[i - 1][temp - a[i]] + a[i]) {
                printf("%d ", a[i]);
                temp -= a[i];
            }
        }
        printf("sum:%d\n", dp[n][v]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yiqzq/article/details/80427829