• 背包专题


    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
    ",m-i);
                        break;
                    }
                }
            } else {
                printf("-1
    ");
            }
        }
        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
    ",dp[0][m]);
        }
    }
  • 相关阅读:
    c#多线程
    把.NET程序部署到没有安装.NET Framwork的机器上
    Java字符编码转换过程说明
    Window 消息大全使用详解
    Regsvr32
    VC++的应用程序框架中各类之间的访问方法
    java接收中文输入并正常显示
    Visual C#中的数据绑定
    截取系统 API 调用(转)
    几个操作文件的API函数
  • 原文地址:https://www.cnblogs.com/Nlifea/p/11745964.html
Copyright © 2020-2023  润新知