zoukankan      html  css  js  c++  java
  • 动态规划 4、基础背包问题总结(多重背包与多重背包的转化)

    描述

    有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

    变式:有的物品能只有1个,有的物品有多个。

    状态转移方程:

    dp[i][j]=max{dp[i][j],dp[i-1][j-c[i]*k]+w[i]*k}//0<=k<=n[i];

    转化成01背包:(目的是便于降低空间复杂度)

    转化成2^n的模版

    int total=p;//p是当前已知物品的数量
    for(int i=1;i<=p;i++){
    	int s=1;
    	while(n[i]>s){
    	       total++;
    	       w[total]=w[i]*s; 
    	       c[total]=c[i]*s
    	       n[i]=n[i]-s;
    	       s=s*2;
            }
    	w[i]=n[i]*i;
    }
    

     空间优化后的01背包模型

    //n:拆分合并后的物品的总数量
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=n;i++){
        for(int j=v;j>=c[i];j--){
            dp[j]=max(dp[j],dp[j-c[i]]+w[i]);
        }
    }
    cout<<dp[v]<<endl;
    //输出了最大容量为v时能达到的最大价值

    然后,谭小奎同志告诉了我一个优化的方法。

    for i=1 to n
     if (c[i]*n[i]>v){
        for j=0 to v
        //当作完全背包来做
    else{
      二进制拆分成01背包
      for(j=v to 0//01背包

    这样来说,数据很好的话,时间复杂度大大降低了。谭小奎同学告诉我完全背包比01背包复杂度低很多,我才注意到时间的问题啊,以前一直没想过~~~~~

     题型分析

     参考01背包那篇博文中的描述。

    1、多重背包的一般性解法,容量不定

    Big Event in HDU

    Description

    Nowadays, we all know that Computer College is the biggest department in HDU. But, maybe you don't know that Computer College had ever been split into Computer College and Software College in 2002.
    The splitting is absolutely a big event in HDU! At the same time, it is a trouble thing too. All facilities must go halves. First, all facilities are assessed, and two facilities are thought to be same if they have the same value. It is assumed that there is N (0<N<1000) kinds of facilities (different value, different kinds).
     

    Input

    Input contains multiple test cases. Each test case starts with a number N (0 < N <= 50 -- the total number of different facilities). The next N lines contain an integer V (0<V<=50 --value of facility) and an integer M (0<M<=100 --corresponding number of the facilities) each. You can assume that all V are different.
    A test case starting with a negative integer terminates input and this test case is not to be processed.
     

    Output

    For each case, print one line containing two integers A and B which denote the value of Computer College and Software College will get respectively. A and B should be as equal as possible. At the same time, you should guarantee that A is not less than B.
     

    Sample Input

    2 10 1 20 1 3 10 1 20 2 30 1 -1
     

    Sample Output

    20 10 40 40
     

    这是一道典型的多重背包问题+性价比为一+装载无定值

    背包容量是各种设备价值总和的一半。这里是可以化成01背包的,为体现多重背包的初始状态,贴上这个程序。

    #include<iostream>
    #include<string.h>
    using namespace std;
    int dp[50+5][125000+5];
    int w[55];
    int nn[55];
    int main(){
        int n;
        while(cin>>n && n>=0){
        if(n==0)cout<<"0"<<" "<<"0"<<endl;
        else{
        int m=0;
        for(int i=1;i<=n;i++){
            cin>>w[i]>>nn[i];
            m+=w[i]*nn[i];
        }
        int mm=m;
        m=m/2;
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        for(int i=1;i<=n;i++){
            for(int j=0;j<=nn[i];j++){
                for(int k=m;k>=0;k--){
                    if(j*w[i]<=k && dp[i-1][k-j*w[i]]==1){
                    dp[i][k]=1;
                    }
                }
            }
        }
        int ans=0;
        for(int i=m;i>=0;i--){
            for(int j=n;j>=1;j--){
                if(dp[j][i]==1) {ans=i;break;}
            }
            if (ans!=0) break;
        }
        cout<<(mm-ans)<<" "<<ans<<endl;
       }
       }
        return 0;
    }

    2、2次拆分的01解法,容量不定

     Dividing

    Description

    Marsha and Bill own a collection of marbles. They want to split the collection among themselves so that both receive an equal share of the marbles. This would be easy if all the marbles had the same value, because then they could just split the collection in half. But unfortunately, some of the marbles are larger, or more beautiful than others. So, Marsha and Bill start by assigning a value, a natural number between one and six, to each marble. Now they want to divide the marbles so that each of them gets the same total value. Unfortunately, they realize that it might be impossible to divide the marbles in this way (even if the total value of all marbles is even). For example, if there are one marble of value 1, one of value 3 and two of value 4, then they cannot be split into sets of equal value. So, they ask you to write a program that checks whether there is a fair partition of the marbles.

    Input

    Each line in the input file describes one collection of marbles to be divided. The lines contain six non-negative integers n1 , . . . , n6 , where ni is the number of marbles of value i. So, the example from above would be described by the input-line "1 0 1 2 0 0". The maximum total number of marbles will be 20000.
    The last line of the input file will be "0 0 0 0 0 0"; do not process this line.

    Output

    For each collection, output "Collection #k:", where k is the number of the test case, and then either "Can be divided." or "Can't be divided.".
    Output a blank line after each test case.

    Sample Input

    1 0 1 2 0 0 
    1 0 0 0 1 1 
    0 0 0 0 0 0 

    Sample Output

    Collection #1:
    Can't be divided.
    
    Collection #2:
    Can be divided.

    由于物品数很多,二维数组超空间,一一分配01做法超时间,我们选择2次拆分来做。
    拆分的模版在上面已经贴出了。接着按照01来做就可以了。
     1 #include<iostream>
     2 #include<string.h>
     3 #define maxn 420000+5
     4 using namespace std;
     5 int dp[maxn];
     6 int w[100];
     7 int main(){
     8     int t=0;
     9     while(1){
    10         t++;
    11         int a[10];
    12         int m=0;
    13         for(int i=1;i<=6;i++){
    14             cin>>a[i];
    15             m=m+i*a[i];
    16         }
    17         if (m%2!=0) {
    18             cout<<"Collection #"<<t<<":"<<endl;
    19             cout<<"Can't be divided."<<endl;
    20             cout<<endl;
    21             continue;
    22         }else
    23         {
    24         m=m/2;
    25         if(a[2]+a[1]+a[3]+a[4]+a[5]+a[6]==0) break;
    26         int total=6;
    27         for(int i=1;i<=6;i++){
    28             int s=1;
    29             while(a[i]>s){
    30                 total++;
    31                 w[total]=i*s; 
    32                 a[i]=a[i]-s;
    33                 s=s*2;
    34             }
    35             w[i]=a[i]*i;
    36         }//现在已经转化成01背包    
    37         memset(dp,0,sizeof(dp));
    38         dp[0]=1;
    39         for(int i=1;i<=total;i++){
    40             for(int j=m;j>=w[i];j--){
    41                 if (dp[j-w[i]])
    42                     dp[j]=1;
    43             }
    44         }
    45         if(!dp[m]) {
    46             cout<<"Collection #"<<t<<":"<<endl;
    47             cout<<"Can't be divided."<<endl;
    48             cout<<endl;
    49         }else{
    50             cout<<"Collection #"<<t<<":"<<endl;
    51             cout<<"Can be divided."<<endl;
    52             cout<<endl;
    53         }
    54     }
    55     }
    56     return 0;
    57 }


  • 相关阅读:
    【Pascal's Triangle II 】cpp
    【Pascal's Triangle】cpp
    【Substring with Concatenation of All Words】cpp
    【Multiply Strings】cpp
    【Minimum Window】cpp
    【Merge Intervals】cpp
    【Insert Interval】cpp
    认识GIT之入门
    数据结构学习系列之线性表(四)
    VBox虚拟机与主机(宿主)通讯原理以及socat(套接字猫)简单介绍
  • 原文地址:https://www.cnblogs.com/little-w/p/3233335.html
Copyright © 2011-2022 走看看