01背包、完全背包

01背包

问题描述

有n个重量(费用)和价值分别为wi,vi的物品。从这些物品中挑选出总重量(费用)不超过W的物品,求所有挑选方案中价值总和的最大值。

例子

n=4

(w,v)={(2,3),(1,2),(3,4),(2,2)}

W=5

输出:7

1 二维数组版:时间复杂度O(nW) ,空间复杂度O(nW)

记得以下所有方法首先都要初始化dp数组memset(dp,0,sizeof(dp));

dp[i][j] :表示从前i个物品中选出总重量不超过j的物品时总价值的最大值。

int dp[MAX_N+1][MAX_W+1];

void solve(){
    for(int i=1;i<=n;i++){
        for(int j=0;j<=W;j++){
            if(j<w[i]){dp[i][j]=dp[i-1][j];}
            else{
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
            }
        }
    }
    printf("%d\n",dp[n][W]);
}

2 一维数组版:时间复杂度O(nW) ,空间复杂度O(W)

上述二维方法可以通过不断重复利用一个一维数组来实现。因为dp[i][j]只可能与dp[i-1][0]到dp[i-1][j]有关,即只与上一行第一列至本列有关,所以内层循环改为由背包容量到物品重量的顺序,即可使用一维数组。

第i层循环,dp[j] :含义同上。

此外,由于少了一维,可省去if(j<w[i]){dp[i][j]=dp[i-1][j];}

int dp[MAX_W+1]

void solve(){
    for(int i=1;i<=n;i++){
        for(int j=W;j>=w[i];j--){
            dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
        }
    }
    printf("%d\n",dp[W]);
}

完全背包

问题描述

有n个重量(费用)和价值分别为wi,vi的物品。从这些物品中挑选出总重量(费用)不超过W的物品,求所有挑选方案中价值总和的最大值。在这里,每种物品可以挑选任意多件。

1 二维数组、三重循环版:时间复杂度O(nW^2),空间复杂度O(nW)

递推关系:

dp[0][j]=0

dp[i][j]=max{dp[i-1][j-k*w[i]]+k*v[i]|0<=k}

此外,当j<w[i]时,属于k=0的情况。

int dp[MAX_N+1][MAX_W+1];

void solve(){
    for(int i=1;i<=n;i++){
        for(int j=0;j<=W;j++){
            for(int k=0;k*w[i]<=j;k++){
                dp[i][j]=max(dp[i][j],dp[i-1][j-k*w[i]]+k*v[i]);
            }
        }
    }
    printf("%d\n",dp[n][W]);
}

2 二维数组、二重循环版:时间复杂度O(nW),空间复杂度O(nW)

思路:

在dp[i][j]的计算中选择k(k>=1)个的情况,与在dp[i][j-w[i]]的计算中选择k-1个的情况是相同的(因为至少必须要选一个物品i)。所以递推关系式可分为不选当前物品,和选至少一个当前物品的情况取最大值。

所以上面递推关系中:

dp[i][j]=max{dp[i-1][j-k*w[i]]+k*v[i]|0<=k}

=max{dp[i-1][j],dp[i][j-w[i]]+v[i]}

注意是上式右侧的一项是dp[i-1][j],表示不选当前物品的情况;代码1中是dp[i][j],dp[i][j]表示不断用最大值更新dp[i][j]。故此方法省去了关于k的循环。

(此外,为什么不会从状态dp[i][j-2*w[i]]+2v[i]转移到状态dp[i][j]?,个人认为因为这样表示将结果分为三种情况,选0个,选1个,至少选2两个,就要写成dp[i][j]=max{dp[i-1][j],dp[i][j-w[i]]+v[i],dp[i][j-2*w[i]]+2v[i]},是繁琐且有赘余计算的。)

void solve(){
    for(int i=1;i<=n;i++){
        for(int j=0;j<=W;j++){
            if(j<w[i]){dp[i+1][j]=dp[i][j];}
            else{
                    dp[i][j]=max(dp[i-1][j],dp[i][j-w[i]]+v[i]);
                }
            }
        }
    }
    printf("%d\n",dp[n][W]);
}

3 一维数组、二重循环版:时间复杂度O(nW),空间复杂度O(W)

因为dp[i][j]只可能与dp[i-1][j]和dp[i][0]到dp[i][j]有关,即只与上一行同一列和本行第一列至本列有关,所以内层循环可以仍为物品重量到背包容量的顺序,并改为使用一维数组。此外,由于少了一维,可省去if(j<w[i]){dp[i][j]=dp[i-1][j];}

int dp[MAX_W+1];

void solve(){
    for(int i=1;i<=n;i++){
        for(int j=w[i];j<=W;j++){
                dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
            }
        }
    }
    printf("%d\n",dp[W]);
}

0-1背包和完全背包对比总结

使用一维数组,两者的算法只有内层遍历顺序不同,但要非常清楚两者的含义完全不同:代码层面,因为内层遍历方向的不同dp[j]一个指dp[i-1][j],一个指dp[i][j]。含义层面,转移到dp[j]的状态来源完全不不同,具体见上。

reference

挑战程序设计竞赛

https://vincentxwd.github.io/blog/2018/08/11/总结-关于01背包和完全背包/

原文地址:https://www.cnblogs.com/coding-gaga/p/10358149.html

时间: 2024-10-10 12:32:54

01背包、完全背包的相关文章

HDU 3033 I love sneakers! (DP 01背包+完全背包)

Problem Description After months of hard working, Iserlohn finally wins awesome amount of scholarship. As a great zealot of sneakers, he decides to spend all his money on them in a sneaker store. There are several brands of sneakers that Iserlohn wan

01背包&amp;完全背包

·01背包&完全背包基础 01背包模型:给定n个物品,第i个物品体积为Wi,价值为Vi,背包容量为sum,选择一些物品放入背包,要求总价值最大. F[i,j]表示前i个物品放入容量为j的包里获得的最大价值. 对于任意一个物品都有两种状态,要么放要么不放,不放的话很显然价值同前,放的话就要从包里拿出一部分体积. 完全背包模型:给定n种物品,第i个物品体积为Wi,价值为Vi,背包容量为sum,选择一些物品放入背包,要求总价值最大. F[i,j]表示前i种物品放入容量为j的包里获得的最大价值. 01背

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",&

POJ3260——The Fewest Coins(多重背包+完全背包)

The Fewest Coins DescriptionFarmer John has gone to town to buy some farm supplies. Being a very efficient man, he always pays for his goods in such a way that the smallest number of coins changes hands, i.e., the number of coins he uses to pay plus

ZOJ 3164 Cookie Choice 分组背包 混合背包

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3181 题意: 就是混合背包加分组背包,有的物品是01背包,有的是多重背包,有的是完全背包,同时物品还有不超过8组的分组,如果在同一组则最多只能选一种.问能不能恰好地用掉D的容量,并且使所获价值最大. 分析: 开始的想法是多开一个下标,先把没有分组的做了,在0的下标,然后分组分别在组号的下标里按顺序处理,是什么背包就用什么做法,不过一直WA,对拍小数据大数据都没啥问题(可能随机

HDU1114_Piggy-Bank(背包/完全背包)

解题报告 题目传送门 题意: 给金币的面额和重量,求装满储蓄罐的最小价值. 思路: 完全背包基础,初始dp为最大,dp[0]=0表示储蓄罐为空价值为0; 状态转移方程就是dp[j]=min(dp[j],dp[j-w[i]]+c[i]) #include <iostream> #include <cstring> #include <cstdio> #define inf 99999999 using namespace std; int main() { int t,i

POJ 3260 多重背包+完全背包

前几天刚回到家却发现家里没网线 && 路由器都被带走了,无奈之下只好铤而走险尝试蹭隔壁家的WiFi,不试不知道,一试吓一跳,用个手机软件简简单单就连上了,然后在浏览器输入192.168.1.1就能看到他的路由器的一切信息,包括密码,然后打开笔记本……好了,废话不多说,能连上网后第一时间当然是继续和队友之前约好的训练了. 今天翻看到之前落下的一道混合背包题目,然后在草稿本上慢慢地写递推方程,把一些细节细心地写好…(本来不用太费时间的,可是在家嘛,一会儿妈走来要我教她玩手机,一会儿有一个亲戚朋

HDU1248_寒冰王座(背包/完全背包)

解题报告 题目传送门 无聊的水题. #include <iostream> #include <cstring> #include <cstdio> #define inf 0x3f3f3f3f using namespace std; int c[10100],w[10100],dp[11000],v,n; int main() { int t,i,j; scanf("%d",&t); while(t--) { memset(dp,0,si

B 二维背包+完全背包 Hdu2159

<span style="color:#3333ff;">/* ------------------------------------------------------------------------------------------------ author : Grant Yuan time : 2014.7.19 aldorithm: 二维背包+完全背包 ----------------------------------------------------

B 维背包+完全背包 Hdu2159

<span style="color:#3333ff;">/* ------------------------------------------------------------------------------------------------ author : Grant Yuan time : 2014.7.19 aldorithm: 二维背包+全然背包 ----------------------------------------------------