背包问题整理

背包问题

给定一组物品,每种物品都有自己的重量和价值,现有一个背包,能承受的重量有限,在受限制的重量下,取若干物品,使得总价值最大。这一类问题,被称为背包问题。

01背包(物品个数为1)

for (int i = 1; i <= N; ++i) {
    for (int j = 0; j <= V; ++j) {
        if(j >= c[i]) {
            dp[i][j] = max(dp[i - 1][j - c[i]] + w[i], dp[i - 1][j]);
        } else {
            dp[i][j] = dp[i - 1][j];
        }
    }
}

时间上是两重循环,时间复杂度为O(NV)。空间是二维的,空间复杂度也为O(NV)。

for (int i = 1; i <= n; ++i)
    for (int j = v; j >= c[i]; --j) {
        dp[j] = max(dp[j - c[i]] + w[i], dp[j]);
    }

这个做法空间复杂度也为O(V)。

多重背包(物品个数有限)

for (int i = 1; i <= N; i++) {
    for (int j = 0; j <= V; j++) {
        for (int k = 0; k <= n[i]; k++) {
            if (j >= c[i] * k) {
                dp[i][j] = max(dp[i - 1][j - c[i] * k] + w[i] * k, dp[i][j]);
            }
        }
    }
}

这一份代码和01背包相比,不再有else部分了,因为,k = 0的时候dp[i][j] = max(dp[i - 1][j], dp[i][j]), 相当于01背包的else部分。

空间优化

既然多重背包的可以转换成01背包,那么我们必然也可以像01背包那样优化空间复杂度。还是按照从大到小的顺序枚举背包体积。

for (int i = 1; i <= N; i++) {
    for (int j = V; j >= 0; j--) {
        for (int k = 1; k <= n[i]; k++) {
            if (j >= c[i] * k) {
                dp[j] = max(dp[j - c[i] * k] + w[i] * k, dp[j]);
            }
        }
    }
}

完全背包(物品个数无限)

解析

虽然物品个数是无限的,但是实际上,由于背包容量有上限,每个物品最多选取的个数也是有限制的,这样可以转换成多重背包问题,进而可以转换成01背包问题。

可以用多重背包的思想来解决完全背包。

for (int i = 1; i <= N; i++) {
    for (int j = 0; j <= V; j++) {
        for (int k = 0; k * c[i] <= j; k++) {
            dp[i][j] = max(dp[i - 1][j - c[i] * k] + w[i] * k, dp[i][j]);
        }
    }
}

for (int i = 1; i <= n; i++) {
    for (int j = 0; j <= v; j++) {
        if (j >= c[i]) {
            dp[i][j] = max(dp[i][j - c[i]] + w[i], dp[i - 1][j]);
        } else {
            dp[i][j] = dp[i - 1][j];
        }
    }
}

这样我们的的算法时间复杂度O(NV),空间复杂度O(NV)。

不难发现,我们也可以把空间复杂度优化下来,优化成O(V)。

for (int i = 1; i <= n; i++) {
    for (int j = c[i]; j <= v; j++) {
        dp[j] = max(dp[j - c[i]] + w[i], dp[j]);
    }
}

与 01 背包相比,完全背包只是第二重循环的顺序发生了翻转。

多重背包的二进制优化

 1 #include <iostream>
 2 using namespace std;
 3 int n[110], c[110], w[110];
 4 int nc[1000], nw[1000];
 5 int dp[5010];
 6 int main() {
 7     int N, V;
 8     cin >> N >> V;
 9     for (int i = 1; i <= N; ++i) {
10         cin >> w[i] >> c[i] >> n[i];
11     }
12     int ncnt = 0;
13     // 二进制拆分
14     for (int i = 1; i <= N; ++i) {
15         int k;
16         // 找到最大的 k
17         for (k = 1; n[i] - (1 << k) + 1 > 0; ++k) {
18             nc[ncnt] = (1 << (k - 1)) * c[i];
19             nw[ncnt] = (1 << (k - 1)) * w[i];
20             ++ncnt;
21         }
22         --k;
23         // 最后一组
24         nc[ncnt] = (n[i] - (1 << k) + 1) * c[i];
25         nw[ncnt] = (n[i] - (1 << k) + 1) * w[i];
26         ++ncnt;
27     }
28     // 01 背包
29     for (int i = 0; i < ncnt; ++i) {
30         for (int j = V; j >= nc[i]; --j) {
31             dp[j] = max(dp[j], dp[j - nc[i]] + nw[i]);
32         }
33     }
34     cout << dp[V] << endl;
35     return 0;
36 }

-

原文地址:https://www.cnblogs.com/jiamian/p/12209162.html

时间: 2024-10-25 11:25:01

背包问题整理的相关文章

背包问题整理;

一.01背包: 1.背景:N件物品,和一个容量为V的背包,每件物品体积为C[i],价值为W[i],求在总体积不超过V的情况下,每件物品可以放或不放,获得的最大价值: 2.分析:我们把问题分成一个小问题,设f[i][v]代表在前i件物品中选择,并且获得容量为v的情况下,所获得最大价值: 则动态转移方程为f[i][v]=max(f[i-1][v],f[i-1][v-c[i]]+w[i]); 3.上述方法时间复杂度为O(V*N),空间复杂度为O(V*N); 优化空间复杂度,可以看到我们的最终答案只在f

背包问题【01、完全(恰好or不超过)、多重】【尚未整理完】

要点: (仨黄色背景代码为三个问题最终用的模板) 1.一维的01背包为啥要逆序(保证更新f[j]时,f[ j - weight[i] ]是没有放入物品i时的数据即f[ i - 1 ][ j - weight[i] ],因为01背包每个物品至多被选一次.而完全背包中,每个物品可以被选无限次,那么状态f[i][j],正好可以由可能已经放入物品i的状态f[ i - 1 ][ j - weight[i] ]转移而来.所以,遍历顺序改为顺序时,就是完全背包问题,其余都不用变~) 2.完全背包 和 01背包

背包问题简单整理

hdu2546,01背包,需要有点变形,计算时需要把价格最大的菜先放一边,最后计算. #include<iostream> #include<cstdio> #include<set> #include<map> #include<vector> #include<iterator> #include<algorithm> #include<cstring> using namespace std; int V

夏令营讲课内容整理 Day 5.

DP专场.. 动态规划是运筹学的一个分支, 求解决策过程最优化的数学方法. 我们一般把动态规划简称为DP(Dynamic Programming) 1.动态规划的背包问题 有一个容量为m的背包,有n个物品,每一个物品i的重量为w[i],价值为v[i]. 要求选择一些物品放入背包中,每种物品只能最多使用一次,使得在不超重的情况下让背包中所有物品价值总和最大. 正常向解法:设状态数组f[i][j]为把前i个物品放入一个容量为j的背包中所能获得的最大价值(以下同设),则状态转移方程为: f[i][j]

DP——背包问题(一)

以前不是很重视 DP ,遇到 DP 就写贪心.暴搜--其实这是非常错误的,现在开始练习 DP 了才发现,我好菜-- 对于DP的整理,先从众所周知的背包问题开始. -------- 01背包:n 个物品,重量和价值分别为 w[i].v[i],背包容量 W,求所有挑选方案中价值总和的最大值. DP 方程 :dp[j] = max ( dp[j] , dp[ j - w[i] ] + v[i] ) ,其中 dp[j]为使用 j 的容量获得的最大价值,i 为第 i 件物品. 代码: 1 #include

【转】国家集训队论文分类整理 作者:洪雁书

距离NOI时间越来越少了,选择性地看一些集训队论文是很有必要的. (在此给已经看过所有论文的神牛跪了= =) 所以,我在此整理了一下,供大家参考. 组合数学 计数与统计 2001 - 符文杰:<Pólya原理及其应用> 2003 - 许智磊:<浅谈补集转化思想在统计问题中的应用> 2007 - 周冬:<生成树的计数及其应用> 2008 - 陈瑜希<Pólya计数法的应用> 数位问题 2009 - 高逸涵<数位计数问题解法研究> 2009 - 刘聪

我自己的Android面试试题总结整理

http://blog.sina.com.cn/s/blog_ad991b1601018mjc.html 2013-5-9号补充:今天最新的腾讯面试题,应该说是所有面试中最难的,我个人感觉.而且是个女面试官,好嗨皮啊,哈哈. 腾讯面试题 1.int a = 1; int result = a+++3<<2; 2.int a = 2; int result = (a++ > 2)?(++a):(a+=3); 3.int a = 1234567; int b = 0x06; int resu

【转】牛人整理分享的面试知识:操作系统、计算机网络、设计模式、Linux编程,数据结构总结

基础篇:操作系统.计算机网络.设计模式 一:操作系统 1. 进程的有哪几种状态,状态转换图,及导致转换的事件. 2. 进程与线程的区别. 3. 进程通信的几种方式. 4. 线程同步几种方式.(一定要会写生产者.消费者问题,完全消化理解) 5. 线程的实现方式. (也就是用户线程与内核线程的区别) 6. 用户态和核心态的区别. 7. 用户栈和内核栈的区别. 8. 内存池.进程池.线程池.(c++程序员必须掌握) 9. 死锁的概念,导致死锁的原因. 10. 导致死锁的四个必要条件. 11. 处理死锁

0-1背包问题与子集合加总问题的近似算法

最近没有怎么更新博客,因为一直比较忙.最近发现所里在做的一个项目中,可以抽出一部分内容和0-1背包问题.子集合加总问题非常相似(虽然表面上不容易看出相似点),所以看了一些这方面的资料和论文,这里主要对问题特点和算法思想做一些整理.这类问题其实很有意思,做数学和做计算机的人都会研究,而且我这里将要提到的论文都是做计算机的人所写的. 问题简述0-1 Knapsack Problem (0-1背包问题,下面简称KP)和Subset Sum Problem (子集合加总问题,下面简称SSP)是经典的NP