完全背包 2016.5.8

一、题目

有 N 种物品和一个容量为 V 的背包,每种物品都有无限件可用

放入第 i 种物品的耗费的空间是 Ci,得到的价值是 Wi

求解:

将哪些物品装入背包,可使这些物品的耗费的空间总和不超过背包容量,且价值总和最大

二、基本思路

这个问题非常类似于01背包问题,所不同的是每种物品有无限件

也就是从每种物品的角度考虑,与它相关的策略已并非取或不取两种

而是有取 0 件、取 1 件、取 2 件……直至取 ?V / Ci? 件等很多种

如果仍然按照解01背包时的思路,令 dp[i,  v] 表示前i种物品恰放入一个容量为 v 的背包的最大权值

仍然可以按照每种物品不同的策略写出状态转移方程

像这样:

dp[i, v] = max{dp[i ? 1, v ? kCi] + kWi | 0 ≤ kCi ≤ v}

这跟01背包问题一样有O(V * N)个状态需要求解

但求解每个状态的时间已经不是常数了,求解状态 dp[i, v] 的时间是 O( v / Ci)

总的复杂度可以认为是 O(N * V * Σv/Ci ),是比较大的

将01背包问题的基本思路加以改进,得到了这样一个清晰的方法

这说明01背包问题的方程的确是很重要,可以推及其它类型的背包问题

但我们还是要试图改进这个复杂度

三、一个简单有效的优化

若两件物品 i、 j 满足 Ci ≤ Cj 且 Wi ≥ Wj ,则将可以将物品 j 直接去掉,不用考虑

这个优化的正确性是显然的:

任何情况下都可将价值小耗费高的j换成物美价廉的 i,得到的方案至少不会更差

对于随机生成的数据,这个方法往往会大大减少物品的件数,从而加快速度

然而这个并不能改善最坏情况的复杂度,因为有可能特别设计的数据可以一件物品也去不掉

这个优化可以简单的O(N ^ 2)地实现,一般都可以承受

另外,针对背包问题而言,比较不错的一种方法是:

首先将费用大于V 的物品去掉,然后使用类似计数排序的做法,计算出费用相同的物品中价值最高的是哪个

可以O(V + N)地完成这个优化

四、转化为01背包问题求解

考虑到第 i 种物品最多选 V / Ci 件

于是可以把第 i 种物品转化为 V / Ci 件费用及价值均不变的物品,然后求解这个01背包问题

这样的做法完全没有改进时间复杂度

但这种方法也指明了将完全背包问题转化为01背包问题的思路:

将一种物品拆成多件只能选 0 件或 1 件的01背包中的物品

更高效的转化方法是:

把第 i 种物品拆成费用为 Ci * 2^k、价值为 Wi * 2^k 的若干件物品,其中 k 取遍满足 Ci * 2^k ≤ V 的非负整数

这是二进制的思想

因为,不管最优策略选几件第 i 种物品,其件数写成二进制后,总可以表示成若干个 2^k 件物品的和

这样一来就把每种物品拆成 O(log(V / Ci) ) 件物品,是一个很大的改进

五、O(V * N)的算法

这个算法使用一维数组,先看伪代码:

dp[0..V ] = 0

for i = 1 to N

for v = Ci to V

dp[v] = max(dp[v],  dp[v ? Ci] + Wi)

你会发现,这个与01背包问题的代码只有v的循环次序不同而已

为什么这个算法就可行呢?

首先想想为什么01背包中要按照 v 递减的次序来循环

让v递减是为了保证第 i 次循环中的状态 dp[i,  v]是由状态 dp[i ? 1,  v ? Ci]递推而来

换句话说,这正是为了保证每件物品只选一次

保证在考虑“选入第 i 件物品”这件策略时,依据的是一个绝无已经选入第 i 件物品的子结果 dp[i ?1,  v ? Ci]

而现在完全背包的特点恰是每种物品可选无限件

所以在考虑“加选一件第 i 种物品”这种策略时,却正需要一个可能已选入第 i 种物品的子结果 dp[i,  v ? Ci]

所以就可以并且必须采用 v 递增的顺序循环

这就是这个简单的程序为何成立的道理

值得一提的是,上面的伪代码中两层for循环的次序可以颠倒

这个结论有可能会带来算法时间常数上的优化

这个算法也可以由另外的思路得出

例如,将基本思路中求解 dp[i, v ? Ci]的状态转移方程显式地写出来

代入原方程中,会发现该方程可以等价地变形成这种形式:

dp[i,  v] = max(dp[i ? 1, v],  dp[i, v ? Ci] + Wi)

将这个方程用一维数组实现,便得到了上面的伪代码

最后抽象出处理一件完全背包类物品的过程伪代码:

def CompletePack(dp, C, W)

for v = C to V

dp[v] = max{dp[v],  dp[v ? C] + W}

六、小结

完全背包问题也是一个相当基础的背包问题,它有两个状态转移方程

希望能够对这两个状态转移方程都仔细地体会

不仅记住,也要弄明白它们是怎么得出来的,最好能够自己想一种得到这些方程的方法

事实上,对每一道动态规划题目都思考其方程的意义以及如何得来

是加深对动态规划的理解、提高动态规划功力的好方法

转自《背包九讲V_2.0》%%%作者,感谢作者

NYOJ 311 完全背包

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int INF = 0x3f3f3f3f;
const int maxn = 2000 + 10;
int C[maxn];
int W[maxn];
int dp[50010];

int main()
{
#ifdef __AiR_H
    freopen("in.txt", "r", stdin);
#endif
    int N;
    scanf("%d", &N);
    while (N--) {
        int M, V;
        scanf("%d%d", &M, &V);
        for (int i = 1; i <= M; ++i) {
            scanf("%d%d", &W[i], &C[i]);
        }
        dp[0] = 0;
        for (int i = 1; i <= V; ++i) {
            dp[i] = -INF;
        }
        for (int i = 1; i <= M; ++i) {
            for (int j = W[i]; j <= V; ++j) {
                dp[j] = max(dp[j], dp[j-W[i]] + C[i]);
            }
        }
        if (dp[V] >= 0) {
            printf("%d\n", dp[V]);
        } else {
            printf("NO\n");
        }
    }
    return 0;
}
时间: 2024-10-16 11:14:44

完全背包 2016.5.8的相关文章

多重背包 2016.5.8

一.题目 有N种物品和一个容量为V 的背包 第i种物品最多有 Mi 件可用,每件耗费的空间是Ci,价值是W 求解将哪些物品装入背包可使这些物品的耗费的空间总和不超过背包容量,且价值总和最大 二.基本算法 这题目和完全背包问题很类似 基本的方程只需将完全背包问题的方程略微一改即可 因为对于第 i 种物品有 Mi+1 种策略: 取0件,取1件--取Mi件 令 dp[i, v]表示前 i 种物品恰放入一个容量为v的背包的最大价值,则有状态转移方程: dp[i, v] = max{dp[i ? 1, v

不管服不服 Windows仍是全球第一大桌面系统

不管服不服 Windows仍是全球第一大桌面系统 近日,根据来自市场调研机构 Net Applications 公布的统计数据显示,Windows 依然是世界上排名第一的操作系统,而且未来将很难被打破,因为现在无论是 Mac OS X 还是 Linux 操作系统的市场份额,均不足以对微软的 Windows 平台产生威胁. 具体而言,截止 2015 年 10 月份,Windows 市场份额仍然高达 90.42%,而 Mac OS X 的份额为 8.00%,Linux 仅有 1.57%. 之前一直有

聊一聊前端模板与渲染那些事儿

欢迎大家收看聊一聊系列,这一套系列文章,可以帮助前端工程师们了解前端的方方面面(不仅仅是代码): https://segmentfault.com/blog/frontenddriver 作为现代应用,ajax的大量使用,使得前端工程师们日常的开发少不了拼装模板,渲染模板.我们今天就来聊聊,拼装与渲染模板的那些事儿. 如果喜欢本文请点击右侧的推荐哦,你的推荐会变为我继续更文的动力 1 页面级的渲染 在刚有web的时候,前端与后端的交互,非常直白,浏览器端发出URL,后端返回一张拼好了的HTML串

使用 Raspberry Pi 上的传感器在 Node.js 中创建一个 IoT Bluemix 应用程序

先决条件 一个IBM Bluemix 帐号,一个 Raspberry Pi 2 或 3,一个 PIR 运动传感器 适用于本文的 Github 存储库 如果您是一位精明的 Bluemix 开发人员,您可能只想看看如何在 node.js 中与 IoT 建立连接,或者只想了解如何从此 github 存储库中拉取我的代码. git clone https://github.com/nicolefinnie/iot-nodejs-tutorial 以下是实现与 IBM IoT 平台连接在一起的 4 个 R

【弱校胡策】2016.4.14 (bzoj2164)最短路+状压DP+矩阵乘法+高斯消元+树链剖分+线段树+背包DP

cyyz&qhyz&lwyz&gryz弱校胡策 命题人:cyyz ws_fqk T3暴力写挫了 50+10+0滚粗辣! 奇妙的约会(appointment.cpp/c/pas) [问题描述] DQS和sxb在网上结识后成为了非常好的朋友,并且都有着惊人 的OI水平.在NOI2333的比赛中,两人均拿到了金牌,并保送进入 HU/PKU.于是两人决定在这喜大普奔的时刻进行面基. NOI2333参赛选手众多,所以安排了n个考点,DQS在1号考点, 而sxb在n号考点.由于是举办全国性赛事

HDU1864(背包)

最大报销额 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 22659    Accepted Submission(s): 6892 Problem Description 现有一笔经费可以报销一定额度的发票.允许报销的发票类型包括买图书(A类).文具(B类).差旅(C类),要求每张发票的总额不得超过1000元,每张发票上,单项物品的价

POJ1384Piggy-Bank[完全背包]

Piggy-Bank Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 10787   Accepted: 5258 Description Before ACM can do anything, a budget must be prepared and the necessary financial support obtained. The main income for this action comes from

POJ1837 Balance[分组背包]

Balance Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 13717   Accepted: 8616 Description Gigel has a strange "balance" and he wants to poise it. Actually, the device is different from any other ordinary balance. It orders two arms

分辣条-01背包恰好装满情况

分辣条 发布时间: 2016年6月26日 20:36   最后更新: 2016年6月26日 20:37   时间限制: 1000ms   内存限制: 128M 描述 "你喝的酸奶是我买的,辣条也是我买的,你现在要跟我分手,你把我当什么?" "因为你每次分辣条的时候都比我多一根!" 可见分好辣条是一件多么重要的事情.. 现在有n(1<=n<=200)根辣条,每根辣条的重量为a1,a2...ai..an(1<=ai<=100). 那么能不能把这些