• 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

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

    完全背包的代码:

    1 void Compack(int w,int v)
    2 {
    3     for(int i=w; i<=V; i++)
    4         if(dp[i]<dp[i-w]+v)
    5             dp[i]=dp[i-w]+v;
    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 w,int v)
     7 {
     8     for(int i=w; i<=V; i++)
     9         if(dp[i]<dp[i-w]+v)
    10             dp[i]=dp[i-w]+v;
    11 }
    12 
    13 void Zeronepack(int w,int v)
    14 {
    15     for(int i=V; i>=w; i--)
    16         if(dp[i]<dp[i-w]+v)
    17             dp[i]=dp[i-w]+v;
    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(w,v);
    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
    ",dp[V]);
    44     }
    45     return 0;
    46 }
  • 相关阅读:
    Spring 依赖注入:简单的HelloWorld例子
    浮动元素margin负值的应用
    小球拖动吸附
    三栏布局
    ES6学习之路1
    绝对定位模拟固定定位效果...
    jQuery中的一些小技巧
    探究css帧动画setps()用处
    你所不知道的cursor妙用
    正则表达式
  • 原文地址:https://www.cnblogs.com/zpfbuaa/p/4991895.html
Copyright © 2020-2023  润新知