背包专题
01背包
01背包是所有背包的基础,很重要也很基础而且最重要的是有很多变形,能够运用得非常灵活,网上有很好的介绍01背包的博客我在这里就不写他的原理和详细步骤了,只是转载一下他们的博客,包括下面的完全背包,多维背包 等等,我想写这篇博客的主要目的是介绍背包的用法,当然了转载的博客是很基础的,因此小白也可以来看
01背包详解
01背包的运用很广泛,也很灵活但是表现在哪里呢
看下这道题
1007 正整数分组
51nod1007
基准时间限制:1 秒 空间限制:131072 KB 分值: 10 难度:2级算法题
将一堆正整数分为2组,要求2组的和相差最小。
例如:1 2 3 4 5,将1 2 4分为1组,3 5分为1组,两组和相差1,是所有方案中相差最少的。
Input
第1行:一个数N,N为正整数的数量。
第2 - N+1行,N个正整数。
(N <= 100, 所有正整数的和 <= 10000)
Output
输出这个最小差
Input示例
5
1
2
3
4
5
Output示例
1
博主在做这道题的时候以为是暴力或者是dfs,足足写了一下午(可能我比较菜QAQ),但是实际上只要将他们的sum值砍一半然后进行01背包就好了,想一想为什么
完全背包
完全背包虽然为背包的一种,但是博主用到的次数不是太多个人认为不太重要,因此不打算做太多解释
就写一个例题好了
多维背包
多维背包可以是01背包, 也可以是完全背包
ACboy needs your help
ACboy has N courses this term, and he plans to spend at most M days on study.Of course,the profit he will gain from different course depending on the days he spend on it.How to arrange the M days for the N courses to maximize the profit?
Input
The input consists of multiple data sets. A data set starts with a line containing two positive integers N and M, N is the number of courses, M is the days ACboy has.
Next follow a matrix A[i][j], (1<=i<=N<=100,1<=j<=M<=100).A[i][j] indicates if ACboy spend j days on ith course he will get profit of value A[i][j].
N = 0 and M = 0 ends the input.
Output
For each data set, your program should output a line which contains the number of the max profit ACboy will gain.
Sample Input
2 2
1 2
1 3
2 2
2 1
2 1
2 3
3 2 1
3 2 1
0 0
Sample Output
3
4
6
这个提有两个权值忍耐度和杀敌数,普通的完全背包是不能够解决的,我们可以用多维背包来解决
我们可以直接在完全背包的基础上加上一维就可以做到二维背包了,那么多维背包同理,加上n-1维就行了
例题代码:
#include <cstdio> //二维背包
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 110;
int w[maxn],v[maxn];
int dp[maxn][maxn];
int main(){
int n,m,s,k;
while(cin>>n>>m>>k>>s){
memset(dp,0,sizeof(dp));
for (int i = 1;i<=k;i++)
scanf("%d %d",&v[i],&w[i]);
for (int i = 1;i<=k;i++) {
for (int j = w[i];j<=m;j++){ //忍耐度
for (int k = 1;k<=s;k++) { //最大杀敌数
dp[j][k] = max(dp[j][k],dp[j-w[i]][k-1] + v[i]);
}
}
}
if ( dp[m][s] >= n ) {
for (int i = 0;i<=m;i++) {
if(dp[i][s]>=n) {
printf("%d\n",m-i);
break;
}
}
} else {
printf("-1\n");
}
}
return 0;
}
分组背包
了解分组背包之前先看一个题
题目描述
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过 NNN 元钱就行”。今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
主件 附件
电脑 打印机,扫描仪
书柜 图书
书桌 台灯,文具
工作椅 无
如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有 000 个、 111 个或 222 个附件。附件不再有从属于自己的附件。金明想买的东西很多,肯定会超过妈妈限定的 NNN 元。于是,他把每件物品规定了一个重要度,分为 555 等:用整数 1−51-51−5 表示,第 555 等最重要。他还从因特网上查到了每件物品的价格(都是 101010 元的整数倍)。他希望在不超过 N 元(可以等于 N 元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第 j 件物品的价格为 v[j],重要度为 w[j] ,共选中了 k 件物品,编号依次为 j1,j2,…,jk ,则所求的总和为:
v[j1]×w[j1]+v[j2]×w[j2]+…+v[jk]×w[jk]。
请你帮助金明设计一个满足要求的购物单。
输入输出格式
输入格式:
第 111 行,为两个正整数,用一个空格隔开:
NmN mNm (其中 N(<32000) 表示总钱数, m(<60)为希望购买物品的个数。) 从第 2 行到第 m+1 行,第 j 行给出了编号为 j−1 的物品的基本数据,每行有 3 个非负整数
vpq (其中 v 表示该物品的价格( v<10000 ),p表示该物品的重要度( 1−5 ), q 表示该物品是主件还是附件。如果 q=0 ,表示该物品为主件,如果 q>0 ,表示该物品为附件, q 是所属主件的编号)
输出格式:
一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值( <200000)。
输入输出样例
输入样例#1:
1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0
输出样例#1:
2200
同多维背包这个题直接用01背包和完全背包是不能直接解的,因为有些物品是有主件的,主件可以独自存活但是附件不可以,这样的话我们可以将一类物品合并成为一组,对这一组整体进行背包,也就符合了01背包,或者完全背包
背包的路径还原
在背包的过程中我们可以加一个vis数组进行路径记录最后用一个ans数组输出
集体实现请看代码,但还请大家仔细思考为什么
最后网盘下载 背包九讲
链接:https://pan.baidu.com/s/1ukO2sOUsUdFqKFvHaJRkEA 密码:ccy7
更新:
树上背包 ( 数型DP )
博主此刻学艺不精,但是通过一个例题跟大家介绍吧
The more, The Better
Problem Description
ACboy很喜欢玩一种战略游戏,在一个地图上,有N座城堡,每座城堡都有一定的宝物,在每次游戏中ACboy允许攻克M个城堡并获得里面的宝物。但由于地理位置原因,有些城堡不能直接攻克,要攻克这些城堡必须先攻克其他某一个特定的城堡。你能帮ACboy算出要获得尽量多的宝物应该攻克哪M个城堡吗?
Input
每个测试实例首先包括2个整数,N,M.(1 <= M <= N <= 200);在接下来的N行里,每行包括2个整数,a,b. 在第 i 行,a 代表要攻克第 i 个城堡必须先攻克第 a 个城堡,如果 a = 0 则代表可以直接攻克第 i 个城堡。b 代表第 i 个城堡的宝物数量, b >= 0。当N = 0, M = 0输入结束。
Output
对于每个测试实例,输出一个整数,代表ACboy攻克M个城堡所获得的最多宝物的数量。
Sample Input
3 2
0 1
0 2
0 3
7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2
0 0
Sample Output
5
13
题解
AC code:
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
vector<int>G[250];
int dp[250][250];
int vis[250];
int n,m;
void dfs(int now) {
vis[now] = 1;
for(int i = 0;i < G[now].size();i++) {
int to = G[now][i];
if(vis[to] == 0) dfs(to);
for(int j = m;j >= 2;j --) {
for(int k = 1;k < j;k ++) {
if( dp[to][j-k] != -1 && dp[now][k] != -1 ) {
dp[now][j] = max( dp[now][j], dp[now][k]+dp[to][j-k] );
}
}
}
}
}
int main() {
while(~scanf("%d%d",&n,&m)) {
if(n == 0 && m == 0)break;
memset(vis ,0 ,sizeof(vis));
memset(dp ,-1 ,sizeof(dp));
for(int i = 0;i <= n;i ++) G[i].clear(),dp[i][0]=0;
dp[0][1] = 0;
for (int i = 1;i <= n;i ++) {
int x,val;
scanf("%d%d",&x,&val);
G[x].push_back(i);
dp[i][1] = val;
}
m++;
dfs(0);
printf("%d\n",dp[0][m]);
}
}