背包专题

 

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;
}

混合背包、二维费用背包、分组背包

分组背包

了解分组背包之前先看一个题

洛谷P1064 金明的预算方案

题目描述

金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过 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]);
    }
}
posted @ 2018-08-08 16:51  Nlifea  阅读(144)  评论(0编辑  收藏  举报