【背包问题】【出来混总是要还的...】总结+入门练手题
花了一晚上加一早上研究背包,唉一大把年纪了才狠下心弄dp也确实说不过去的......
背包入门当然还是看背包九讲(链接很多,没找到原作的,就随便贴一个链接了...),我再扯也是班门弄斧,只是贴一些摘要以及写代码时候的总结吧。
01背包:有N件物品和一个容量为V的背包。第i件物品的体积是v[i],价值是val[i],每种只有一件。求解将哪些物品装入背包可使价值总和max_val最大。
max_val[i][j] --> dp[i][j] : 从前i个物品中选择重量不超过j的物品时的最大价值;
max_val[i][0] = max_val[0][j] = 0; ( i~{1,N}, j~{0,V}; )
只考虑第i件物品的策略
max_val[i][j] = ① max_val[i-1][j]; j > v[i]
② max{ max_val[i-1][j], max_val[i-1][j-v[i]]+val[i] }; j <= v[i];
②的判断过程:1) 计算不放入该物品时该重量的最大价值;
2) 计算当前物品的价值 + 可以容纳的剩余重量的最大价值;
3) 找到二者之中的最大值。
解决了所有的子问题之后,返回max_val
[W]的值——N件物品重量为W时最大价值;
状态转移方程也可以用一维数组表示(切记01背包是逆序求解,稍后的完全背包是顺序求解):
void zero_one_pack(int cost, int value) { for(int i = sum;i >= cost;i--) dp[i] = max(dp[i],dp[i-cost]+value); } for(int i = 1; i <= n; i++) { zero_one_pack(v[i], val[i]); }
完全背包: 有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的体积是v[i],价值是val[i]。求将哪些物品装入背包可使这些物品的总体积不超过背包容量,且价值总和最大。
基本跟01背包一个思路,只不过多了些变化:
max_val[i][j] --> dp[i][j] : 从前i个物品中选择重量不超过j的物品时的最大价值;
max_val[i][0] = max_val[0][j] = 0; ( i~{1,N}, j~{0,V}; )
只考虑第i件物品的策略
max_val[i][j] = ① max_val[i-1][j]; j > v[i]
② max{ max_val[i-1][j-k*v[i]]+k*val[i] | k >= 0 };
=max{ max_val[i-1][j], max{max_val[i-1][j-k*v[i]]+k*val[i] | k >= 1} };
=max{ max_val[i-1][j], max{max_val[i-1][(j-v[i])-k*v[i]]+ val[i] + k*val[i] | k >= 0} };
=max{ max_val[i-1][j], max{max_val[i-1][(j-v[i])-k*v[i]] + k*val[i] | k >= 0} + val[i] };
=max{ max_val[i-1][j], max_val[i][j-v[i]] + val[i] };
注意与01背包的区别--01背包的第一维是i-1,而完全背包的第一维是i;因此完全背包的一维数组表示时是顺序求解;
void complete_pack(int cost,int value) { for(int i = cost; i <= sum; i++) dp[i] = max(dp[i], dp[i-cost]+value); } for(int i = 1; i <= n; i++) { complete_pack(v[i], val[i]); }
多重背包:有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件体积是v[i],价值是val[i];
①基本算法
max_val[i][j] = ① max_val[i-1][j]; j > v[i]
② max{ max_val[i-1][j-k*v[i]]+k*val[i] | 0<=k<=n[i] };
=max{ max_val[i-1][j], max{max_val[i-1][j-k*v[i]]+k*val[i] | 1<=k<=n[i]} };
=max{ max_val[i-1][j], max{max_val[i-1][(j-v[i])-k*v[i]]+ val[i] + k*val[i] | 0<=k<n[i]} };
=max{ max_val[i-1][j], max{max_val[i-1][(j-v[i])-k*v[i]] + k*val[i] | 0<=k<n[i]} + val[i] };
=max{ max_val[i-1][j], max_val[i][j-v[i]] + val[i] };
复杂度是O(V*Σn[i])
② 转化为01背包问题:把每种物品的n[i]件看成n[i]件01背包中不同的物品,则得到了物品数为Σn[i]的01背包问题,直接求解,复杂度也是O(V*Σn[i])
eg. hdu-2191
/* Problem: hdu2159 Tips : 二维费用完全背包 Dyadic_Complete_Pack Date : 2015.8.8 */ #include <cstdio> #include <iostream> #include <sstream> #include <cmath> #include <cstring> #include <cstdlib> #include <string> #include <vector> #include <map> #include <set> #include <queue> #include <stack> #include <algorithm> using namespace std; #define ll long long #define _cle(m, a) memset(m, a, sizeof(m)) #define repu(i, a, b) for(int i = a; i < b; i++) #define repd(i, a, b) for(int i = b; i >= a; i--) #define sfi(n) scanf("%d", &n) #define sfl(n) scanf("%I64d", &n) #define pfi(n) printf("%d\n", n) #define pfl(n) printf("%I64d\n", n) const int inf = 0x3f3f3f3f; const int maxn = 105; int n, m, k, s; int dp[maxn][maxn]; //dp[i][j]i忍耐度,j杀怪数,经验值 void Dyadic_Complete_Pack(int a, int b) { for(int i = b; i <= m; i++) for(int j = 1; j <= s; j++) dp[i][j] = max(dp[i][j], dp[i-b][j-1]+a); } int main() { while(~scanf("%d%d%d%d", &n, &m, &k, &s)) { memset(dp, 0, sizeof(dp)); for(int i = 1; i <= k; i++) { int a, b; scanf("%d%d", &a, &b); Dyadic_Complete_Pack(a, b); } if(dp[m][s] < n) printf("-1\n"); else { int ans = 0; for( ; ans <= m; ans++) if(dp[ans][s] >= n) break; printf("%d\n", m-ans); } } return 0; }hdu_2159
更多拓展,日后边做边积累~
- 关键字 static和final
- 进程和线程的差别
- C++(33)模板与泛型之实例化
- android代码签名和混乱的包装
- Android 5.1 Phone MO(去电)流程分析(Framework层)
- 暂无
- OC 写一个方法
- 匹配算法重载方法
- Python匿名函数——lambda表达式
- linux笔记
- 正则表达式经典实例
- Java网络编程
- C++(32)
- JSP简单的练习-使用JDOM阅读xml文件
- Python生成器
- 【python网络编程】新浪爬虫:关键词搜索爬取微博数据
- 在网页动态添加html代码时,使用jQuery调用隐藏的事件.
- was mutated while being enumerated
- Code Forces 525C Ilya and Sticks
- UI中简单的猜数字游戏