zoukankan      html  css  js  c++  java
  • 算法:零一背包问题

    零一背包问题是一个非常重要的动态规划求解的问题,只要你学习DP就不可不学零一背包,所以务必要认真学习这个问题。

    例题

    hdu2602 Bone Collector

    题目描述:
    "骨头收集者"带着体积V的背包去捡骨头,已知每个骨头的体积和价值,求能装进背包的最大价值。N <= 1000, V <= 1000。

    输入格式:
    第一行:是测试数据。
    接下来对于每组测试数据,第一行是骨头数量和背包体积,第二行是每个骨头的价值,第三行是每个骨头的体积。

    输出格式:
    一个整数,为最大价值总和是多少。

    输入样例:

    1
    5 10
    1 2 3 4 5
    5 4 3 2 1
    

    输出样例:

    14
    

    零一背包问题

    看到这道题,想想暴力、贪心貌似都不行,那么此时就要来dp了。而提到DP,就一定就要去想:状态、转移方程、初值和答案了。

    1. 状态:dp[i] [j]指到第i个物品,重量为j时,价值总和的最大值。
    2. 转移方程:dp[i] [j] = max(dp[i - 1] [j], dp[i - 1] [j - w[i]] + v[i])
      其实这个的意思就是:分两种情况讨论,如果说不选这第i个物品,那么直接把dp[i] [j] = dp[i - 1] [j]就行了,但是如果说选这个物品,那么就得分析一下了,先看重量:要找dp[i] [j],就必然是由dp[i] [j - w[i]]推过来的,而不是dp[i] [j + w[i]]为什么呢?不妨设dp[i] [j]是由dp[i] [x]推过来的,上一个重量x是加上了一个w[i]才得到现在的重量j的值的,所以说x + w[i] = j,根据等式的性质2,得出x = j - w[i]。接下来就比较简单了,价值让dp[i] [j - w[i]] + v[i]就好了。最后,因为要取价值的最大值,就让两种情况取个max就没问题了。
    3. 初值:dp[i] = 0;
    4. 答案:dp[n] [m]
      考虑到最后一个数得出的答案,一定是分析完所有的数了,所以必然是最大值。

    这是按照题目中所述样例制作而成的表格(第j行第i列表示dp[i] [j]的大小可以借鉴)。
    在这里插入图片描述
    但是二维动态规划消耗的内存太大了,我们想去优化空间,那么我们当然会想到直接将二维数组降成一维数组了。可如果直接改我们会发现这样不对的,为什么呢?

    因为,dp[j]先被dp[j - w[i]]更新了,接着dp[j + 1]被dp[j - w[i] + 1]更新了......dp[j + w[i]]被dp[j]更新了,可是dp[j]已经被更新了,而我们需要的是更新之前的dp[j],这样就有了后效性。所以解决方法就是倒着循环就好了,for j = m to w[i],这样dp[j + w[i]]先被dp[j]更新了,后来dp[j]才被dp[j - w[i]]更新。

    代码

    # include <cstdio>
    # include <cmath>
    # include <cstring>
    # include <algorithm>
    
    using namespace std;
    
    const int N_MAX = 1000, M_MAX = 1000;
    
    int n, m;
    int w[N_MAX+ 10], v[N_MAX + 10];
    int dp[M_MAX + 10];
    
    int package()
    {
    	memset(dp, 0, sizeof(dp));
    	for (int i = 1; i <= n; i++)
    		for (int j = m; j >= w[i]; j--)
    			dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
    	return dp[m];
    }
    
    int main()
    {
    	int T;
    	scanf("%d", &T);
    	while (T--) {
    		scanf("%d%d", &n, &m);
    		for (int i = 1; i <= n; i++)
    			scanf("%d", &v[i]);
    		for (int i = 1; i <= n; i++)
    			scanf("%d", &w[i]);
    		printf("%d
    ", package());
    	}
    	return 0;
    }
    
  • 相关阅读:
    Excelpackage的简单使用(导出用户信息并保存)
    set nocount on/off的作用,可配合存储过程使用
    在sql中case子句的两种形式
    C#开发微信门户及应用(1)--开始使用微信接口(转)
    张建总的一封信
    Jquery在线工具
    《EnterLib PIAB深入剖析》系列博文汇总 (转)
    微软企业库5.0 学习之路——第十步、使用Unity解耦你的系统—PART1——为什么要使用Unity? (转)
    结合领域驱动设计的SOA分布式软件架构 (转)
    ENode框架旨在帮助我们开发ddd,cqrs、eda和事件采购风格的应用程序。(netfocus 汤雪华)
  • 原文地址:https://www.cnblogs.com/000zwx000/p/12468282.html
Copyright © 2011-2022 走看看