DP大作战——多重背包

题目描述

在之前的上机中,零崎已经出过了01背包和完全背包,也介绍了使用-1初始化容量限定背包必须装满这种小技巧,接下来的背包问题相对有些难度,可以说是01背包和完全背包的进阶问题。

多重背包:物品可以有0-n件。

对于第i种物品,我们有取0件,1件…n [ i ] 件共n [ i ] +1种策略,状态转移方程为f [ i ] [ v ] = max { f [ i - 1 ] [ v - k × c [ i ] ] + k × w [ i ] | 0 <=k<= n [ i ] }。在这里,很自然的有一种策略可以将其转化为01背包,即将物品换为n[i]件01背包中的物品,但是复杂度为O(VΣni),时间复杂度没有降低。实际上,对于所有类似情况,我们都可以利用二进制求和来降低时间复杂度。即将物品替换为价值和费用 * 系数=1,2,2^2,…,2^k,n[i]-2^k+1的物品。系数之和为n [ i ],表明不能取到多于n [ i ]件物品,但可以取到0…n[ i ]中任意一个整数件。利用这一优化,算法事件复杂度可以降到O(VΣlogni)。

实际上F [ i ] [ j ] 只依赖于 F [ i-1 ] [ j - k * w [ i ] ],这里依赖项之间构成了一个 { j mod w [ i ] }剩余类,不同剩余类之间无关,注意到这点利用单调队列,每个状态均摊O(1)的时间,可以进一步将算法时间复杂度优化至O(VN)级别的,不过在此不再详细阐述。(其实也就是NOIP程度,放在大学应该可以接受,但是这个优化个人感觉已经脱离dp)

DD大牛给出的伪代码。
def MultiplePack(F,C,W,M)
    if C * M >= V
        CompletePack(F,C,W)
        return //考虑这里为什么可以直接用完全背包
    k := 1
    while k < M
        ZeroOnePack(kC,kW)
        M := M - k
        k := 2k
        ZeroOnePack(C M,W M)

输入

第一个数为数据组数n 1<=n<=10

接下来n组测试数据,每组测试数据由2部分组成。

第一行为背包容量V,物品种类数N。1<=V<=30000,1<=N<=200

接下来N行每行三个数为物品价值v,物品重量w,物品件数M。

1<=v,w<=200, 1<=M<=25

输出

对于每组数据,输出一行,背包能容纳的最大物品价值

输入样例

1
10 2
1 2 3
2 3 2

输出样例

6

解题思路:问题属于背包问题,同时包括了0-1和完全背包,因此为多重背包问题。按照之前的想法,只要判断每件物品的件数,可以确定对于该物品是使用0-1背包还是完全背包。0-1背包的代码:
1 void Zeronepack(int v,int w)
2 {
3     for(int i=V; i>=v; i--)
4         if(dp[i]<dp[i-v]+w)
5             dp[i]=dp[i-v]+w;
6 }

完全背包的代码:

1 void Compack(int v,int w)
2 {
3     for(int i=v; i<=V; i++)
4         if(dp[i]<dp[i-v]+w)
5             dp[i]=dp[i-v]+w;
6 }

本题需要利用0-1背包以及完全背包来解决多重背包问题

代码:

 1 #include <bits/stdc++.h>
 2 #include<stdio.h>
 3 #include<string.h>
 4 int dp[30005];
 5 int V,N;
 6 void Compack(int v,int w)
 7 {
 8     for(int i=v; i<=V; i++)
 9         if(dp[i]<dp[i-v]+w)
10             dp[i]=dp[i-v]+w;
11 }
12
13 void Zeronepack(int v,int w)
14 {
15     for(int i=V; i>=v; i--)
16         if(dp[i]<dp[i-v]+w)
17             dp[i]=dp[i-v]+w;
18 }
19
20 int main()
21 {
22     int kase,v,w,m;
23     scanf("%d",&kase);
24     while(kase--)
25     {
26         memset(dp,0,sizeof(dp));
27         scanf("%d%d",&V,&N);
28         for(int i=1; i<=N; i++)
29         {
30             scanf("%d%d%d",&v,&w,&m);
31             if(w*m>=V)
32                 Compack(v,w);
33             else
34             {
35                 for(int j=1; j<m; j<<1)
36                 {
37                     Zeronepack(j*w,j*v);
38                     m-=j;
39                 }
40                 Zeronepack(m*w,m*v);
41             }
42         }
43         printf("%d\n",dp[V]);
44     }
45     return 0;
46 }
时间: 2025-01-05 17:08:45

DP大作战——多重背包的相关文章

Codeforces 1132E(大数据多重背包)

题目链接 题意 给定背包容量$w$,体积分别为$1$到$8$的物体的数量求不超过背包容量的最大体积 思路 考虑将答案转化成$840 * x + y$的形式其中$840 = LCM(1-8), y < 840 * 8$,想法是对于选定的方案所有种类的物品都可以被表示成$840 * t_i + k_i$的形式,那么我们只用考虑每种物品凑不够$840$的那些物品的选取情况,如果能够知道这些选取情况下对应的能取到的最大的$840$份数就能得到答案.所以$dp[i][j]$表示已经考虑完前$i - 1$种

vijos P1836HYS与七夕节大作战 (01背包之2--转换dp对象)

题目:vijos P1836HYS与七夕节大作战 题意: n个对象,每价值为vi,比重pi,总容量100 分析: 类似背包重量的比重pi为实数,不能作为下标,所以改变dp对象 将求容量100内的最大价值 → 求相应价值的最小容量, 则容量第一个≤100的价值,为符合条件的价值最大的值 状态:dp[v]:价值为v的最小容积 转移方程:         dp[V] = min(dp[V], dp[V-v[i]] + p[i]); 核心: for(i = 1; i<=n; i++) {     for

HDU2191_悼念512汶川大地震遇难同胞——珍惜现在,感恩生活(背包/多重背包)

解题报告 题目传送门 题意: 中文不多说; 思路: 基础多重背包,每个物品有多个可以选,转换成01背包解. #include <iostream> #include <cstring> #include <cstdio> #define inf 99999999 using namespace std; int main() { int t,i,j,n,m,v,p,h,cc,w[1010],c[1010],dp[1010]; scanf("%d",&

hdu 2191 多重背包 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活

http://acm.hdu.edu.cn/showproblem.php?pid=2191 New~ 欢迎“热爱编程”的高考少年——报考杭州电子科技大学计算机学院关于2015年杭电ACM暑期集训队的选拔 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 17930    Accepted

杭电 HDU ACM 2191 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活(多重背包)

悼念512汶川大地震遇难同胞--珍惜现在,感恩生活 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 19574    Accepted Submission(s): 8285 Problem Description 急!灾区的食物依然短缺! 为了挽救灾区同胞的生命,心系灾区同胞的你准备自己采购一些粮食支援灾区,现在假设你一共有资金n元,

hdu1059 dp(多重背包二进制优化)

hdu1059 题意,现在有价值为1.2.3.4.5.6的石头若干块,块数已知,问能否将这些石头分成两堆,且两堆价值相等. 很显然,愚蠢的我一开始并想不到什么多重背包二进制优化```因为我连听都没有听过```不得不吐槽自己的知识面太窄```于是,我用了母函数写这题,母函数的做法并没有问题,但是由于这道题的数据很大,母函数轻轻松松就超时了,于是,我又很努力地在母函数循环的优化上面想出路,改改改,各种改之后依旧TLE,01背包的做法显然也是会超时的,DISCUSS里的母函数做法优化方式都是模上一个大

悼念512汶川大地震遇难同胞――珍惜现在,感恩生活(背包九讲_多重背包)

悼念512汶川大地震遇难同胞――珍惜现在,感恩生活Crawling in process... Crawling failed Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status Description 急!灾区的食物依然短缺! 为了挽救灾区同胞的生命,心系灾区同胞的你准备自己采购一些粮食支援灾区,现在假设你一共有资金n元,而市场有m种大米,每种大米都是袋装产品,其价

POJ 1742 Coins ( 经典多重部分和问题 &amp;&amp; DP || 多重背包 )

题意 : 有 n 种面额的硬币,给出各种面额硬币的数量和和面额数,求最多能搭配出几种不超过 m 的金额? 分析 : 这题可用多重背包来解,但这里不讨论这种做法. 如果之前有接触过背包DP的可以自然想到DP数组的定义 ==> dp[i][j] 表示使用前 i 种硬币是否可以凑成面额 j . 根据这样的定义,则一开始初始化 dp[0][0] = true 最后统计 dp[n][1 ~ m] 为 true 的数量即为答案 状态转移方程为 dp[i][j] |= dp[i-1][ j - k*val[i

DZY Loves Math II:多重背包dp+组合数

Description Input 第一行,两个正整数 S 和 q,q 表示询问数量.接下来 q 行,每行一个正整数 n. Output 输出共 q 行,分别为每个询问的答案. Sample Input 30 3 9 29 1000000000000000000 Sample Output 0 9 450000036 Hint 感谢the Loser协助更正数据对于100%的数据,2<=S<=2e6??,1<=n<=101810^{18}10?18??,1<=q<=10