01背包问题动态规划详解

合集下载

01背包问题动态规划详解

01背包问题动态规划详解

动态规划是用空间换时间的一种方法的抽象。

其关键是发现子问题和记录其结果。

然后利用这些结果减轻运算量。

比如01背包问题。

因为背包最大容量M未知。

所以,我们的程序要从1到M一个一个的试。

比如,开始任选N件物品的一个。

看对应M的背包,能不能放进去,如果能放进去,并且还有多的空间,则,多出来的空间里能放N-1物品中的最大价值。

怎么能保证总选择是最大价值呢?看下表。

测试数据:10,33,44,55,6c[i][j]数组保存了1,2,3号物品依次选择后的最大价值.这个最大价值是怎么得来的呢?从背包容量为0开始,1号物品先试,0,1,2,的容量都不能放.所以置0,背包容量为3则里面放4.这样,这一排背包容量为4,5,6,....10的时候,最佳方案都是放4.假如1号物品放入背包.则再看2号物品.当背包容量为3的时候,最佳方案还是上一排的最价方案c为4.而背包容量为5的时候,则最佳方案为自己的重量5.背包容量为7的时候,很显然是5加上一个值了。

加谁??很显然是7-4=3的时候.上一排c3的最佳方案是4.所以。

总的最佳方案是5+4为9.这样.一排一排推下去。

最右下放的数据就是最大的价值了。

(注意第3排的背包容量为7的时候,最佳方案不是本身的6.而是上一排的9.说明这时候3号物品没有被选.选的是1,2号物品.所以得9.)从以上最大价值的构造过程中可以看出。

f(n,m)=max{f(n-1,m), f(n-1,m-w[n])+P(n,m)}这就是书本上写的动态规划方程.这回清楚了吗?下面是实际程序:#include<stdio.h>int c[10][100];int knapsack(int m,int n){int i,j,w[10],p[10];for(i=1;i<n+1;i++)scanf("\n%d,%d",&w[i],&p[i]);for(i=0;i<10;i++)for(j=0;j<100;j++)c[i][j]=0;for(i=1;i<n+1;i++)for(j=1;j<m+1;j++){if(w[i]<=j){if(p[i]+c[i-1][j-w[i]]>c[i-1][j])c[i][j]=p[i]+c[i-1][j-w[i]];elsec[i][j]=c[i-1][j];}else c[i][j]=c[i-1][j];}return(c[n][m]);}int main(){int m,n;int i,j;scanf("%d,%d",&m,&n);printf("Input each one:\n");printf("%d",knapsack(m,n));printf("\n");for(i=0;i<10;i++)for(j=0;j<15;j++){printf("%d ",c[i][j]);if(j==14)printf("\n");}system("pause");}-----------------------------------------------------------------------------------------------------------------------------------题目有N件物品和一个容量为V的背包。

动态规划之01背包问题(最易理解的讲解)

动态规划之01背包问题(最易理解的讲解)

01背包问题,是用来介绍动态规划算法最经典的例子,网上关于01背包问题的讲解也很多,我写这篇文章力争做到用最简单的方式,最少的公式把01背包问题讲解透彻。

01背包的状态转换方程f[i,j] = Max{ f[i-1,j-Wi]+Pi( j >= Wi ), f[i-1,j] }f[i,j]表示在前i件物品中选择若干件放在承重为j 的背包中,可以取得的最大价值。

Pi表示第i件物品的价值。

决策:为了背包中物品总价值最大化,第i件物品应该放入背包中吗?题目描述:有编号分别为a,b,c,d,e的五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,现在给你个承重为10的背包,如何让背包里装入的物品具有最首先要明确这张表是从右到左,至底向上生成的。

为了叙述方便,用e10单元格表示e行10列的单元格,这个单元格的意义是用来表示只有物品e时,有个承重为10的背包,那么这个背包的最大价值是6,因为e物品的重量是4,背包装的了,把e装进去后价值为6。

然后是e9单元格表示背包承重9,只有物品e, e装进去后,背包价值为6,接着是e8, e7单元格,一直到e3单元格表示背包承重3,但物品e承重4,装不了,所以e3=0,对于d10单元格,表示只有物品e,d时,承重为10的背包,所能装入的最大价值,是10,因为物品e,d这个背包都能装进去。

对于承重为9的背包,d9=10,是怎么得出的呢?根据01背包的状态转换方程,需要考察两个值,一个是f[i-1,j],对于这个例子来说就是e9的值6,另一个是f[i-1,j-Wi]+Pi;在这里,f[i-1,j]表示我有一个承重为9的背包,当只有物品e可选时,这个背包能装入的最大价值f[i-1,j-Wi]表示我有一个承重为4的背包(等于当前背包承重减去物品d的重量),当只有物品e可选时,这个背包能装入的最大价值f[i-1,j-Wi]就是指单元格e4值为6,Pi指的是d物品的价值,即4由于f[i-1,j-Wi]+Pi = 6 + 4 = 10 大于f[i-1,j] = 6,所以物品d应该放入承重为9的背包,所以d9=10.。

动态规划——01背包问题

动态规划——01背包问题

动态规划——01背包问题⼀、最基础的动态规划之⼀01背包问题是动态规划中最基础的问题之⼀,它的解法完美地体现了动态规划的思想和性质。

01背包问题最常见的问题形式是:给定n件物品的体积和价值,将他们尽可能地放⼊⼀个体积固定的背包,最⼤的价值可以是多少。

我们可以⽤费⽤c和价值v来描述⼀件物品,再设允许的最⼤花费为w。

只要n稍⼤,我们就不可能通过搜索来遍查所有组合的可能。

运⽤动态规划的思想,我们把原来的问题拆分为⼦问题,⼦问题再进⼀步拆分直⾄不可再分(初始值),随后从初始值开始,尽可能地求取每⼀个⼦问题的最优解,最终就能求得原问题的解。

由于不同的问题可能有相同的⼦问题,⼦问题存在⼤量重叠,我们需要额外的空间来存储已经求得的⼦问题的最优解。

这样,可以⼤幅度地降低时间复杂度。

有了这样的思想,我们来看01背包问题可以怎样拆分成⼦问题:要求解的问题是:在n件物品中最⼤花费为w能得到的最⼤价值。

显然,对于0 <= i <= n,0 <= j <= w,在前i件物品中最⼤花费为j能得到的最⼤价值。

可以使⽤数组dp[n + 1][w + 1]来存储所有的⼦问题,dp[i][j]就代表从前i件物品中选出总花费不超过j时的最⼤价值。

可知dp[0][j]值⼀定为零。

那么,该怎么递推求取所有⼦问题的解呢。

显⽽易见,要考虑在前i件物品中拿取,⾸先要考虑前i - 1件物品中拿取的最优情况。

当我们从第i - 1件物品递推到第i件时,我们就要考虑这件物品是拿,还是不拿,怎样收益最⼤。

①:⾸先,如果j < c[i],那第i件物品是⽆论如何拿不了的,dp[i][j] = dp[i - 1][j];②:如果可以拿,那就要考虑拿了之后收益是否更⼤。

拿这件物品需要花费c[i],除去这c[i]的⼦问题应该是dp[i - 1][j - c[i]],这时,就要⽐较dp[i - 1][j]和dp[i - 1][j - c[i]] + v[i],得出最优⽅案。

01背包问题理解动态规划算法

01背包问题理解动态规划算法

01背包问题理解动态规划算法⼀.动态规划算法简单理解:在⼀些分治算法解决的问题中,需要将较⼤规模的问题转化为较⼩规模的问题,往往会⽤到递归。

但是在⼀些问题中,递归的⼩问题被多次重复运算,浪费了性能,因此可以使⽤数组或者其他合适的⽅式将运算过的⼩规模问题的结果记录下来,再运算⼩规模的问题时先看是不是已经运算过了,没有运算过再去运算并将结果保存,所以⼀般分治算法都是从⼤规模问题开始递归运算,不断将问题规模变⼩,⽽动态规划算法则⼀般从⼩规模问题开始运算,每次运算分解出的⼩规模问题都是已经运算过的。

如此便使⽤了存储空间换取了运算时间,减⼩了时间复杂度。

⼆.01背包问题1.穷举法可以先不考虑背包,考虑被拿⾛的物品的所有组合情况,n个物品有2n种拿法(每个物品都可以选择拿或者不拿),然后再判断每⼀种拿法是否能放⼊背包,能放⼊背包的情况下计算总价值并⽐较出最⼤价值。

static void Main(string[] args){//记录重量的数组,物品有3种重量,分别是3、4、5int[] w = { 0, 3, 4, 5 };//记录物品价值的数组,和重量数组对应,物品价值分别是4、5、6int[] p = { 0, 4, 5, 6 };//调⽤Exhaustivity函数得到7kg的背包放置物品的最⼤价值Console.WriteLine(Exhaustivity(9 , w, p));Console.ReadKey();}///<summary>///计算给定的mkg背包放置物品的最⼤价值///</summary>///<param name="m">给定的物品重量</param>///<param name="w">记录所有物品重量的数组</param>///<param name="p">记录所有物品对应价格的数组</param>///<returns>背包中放置物品的最⼤价值</returns>public static int Exhaustivity(int m,int[] w,int[] p){//记录放⼊物品的最⼤的价格int maxPrice = 0;//有3个物品,外层就循环2的3次⽅,即i值从0取到7,i的⼆进制数字最多3位,每⼀个i的取值的⼆进制数字都对应3个物品是否放⼊背包//如i为6,⼆进制为110,代表取前两个物品,不取第3个物品//⼜如i为0,⼆进制为000,代表3个物品均不取;i为1,⼆进制001,代表只取第3个物品//通过这种⽅式穷举所有物品放⼊背包的情况,然后判断每⼀种情况重量是否满⾜要求,再⽐较价格for(int i = 0;i < Math.Pow(2,w.Length - 1); i++){//记录所有被放⼊背包物品的总重量int weightTotal = 0;//记录所有被放⼊背包物品的总价格int priceTotal = 0;//内层循环负责计算并⽐较所有物品的总重量和总价格//内层循环的次数就是物品个数,然后分别判断当前情况下每个物品是否放⼊背包,j代表第⼏个物品for(int j = 1;j <= w.Length - 1; j++){//计算当前物品是否放⼊背包int result = Get2(i, j);//在放⼊背包的情况下,将重量和价格累加到总重量和总价格上if(result == 1){weightTotal += w[j];priceTotal += p[j];}}//判断是否超重,不超重的情况下判断价格是不是⼤于最⼤价格,如果是更新最⼤价格if (weightTotal <= m && priceTotal > maxPrice)maxPrice = priceTotal;}//返回最⼤价格return maxPrice;}///<summary>///通过按位与运算得到给定物品number在给定放⼊情况i中是否放⼊背包///</summary>///<param name="i">给定的物品放⼊情况</param>///<param name="number">给定的物品编号</param>///<returns></returns>public static int Get2(int i,int number){int A = i;int B = (int)Math.Pow(2,number - 1);int result = A & B;if (result == 0)return0;return1;}2.分治算法(⾃上⽽下分解问题)这个问题的分治算法的核⼼是将物品逐个放⼊背包,有以下⼏种情况:1)背包没有剩余重量了2)当前物品的编号为0,即所有的物品都试过了3)当前物品太重,⽆法放⼊背包4)当前物品可以放⼊背包在第4)种情况下,有两种情况,⼀种放⼊物品得到的价格最⼤,⼀种不放⼊物品得到的价格最⼤(因为放⼊物品后背包的剩余可放⼊重量也变⼩了,所以放⼊当前物品对后续物品能否放⼊的情况有影响,因此不⼀定是将当前物品放⼊得到的总价格最⾼,需要实际求解并判断),因此需要进⾏⽐较得到最⼤价格static void Main(string[] args){//记录重量的数组,物品有3种重量,分别是3、4、5int[] w = { 0, 3, 4, 5 };//记录物品价值的数组,和重量数组对应,物品价值分别是4、5、6int[] p = { 0, 4, 5, 6 };Console.WriteLine(UpDown(9, w.Length - 1, w, p));Console.ReadKey();}///<summary>///根据背包剩余重量和物品编号计算放⼊物品能获得最⼤价值还是不放⼊物品能获得最⼤价值///</summary>///<param name="m">背包剩余能装的重量</param>///<param name="i">当前放⼊背包的物品编号</param>///<param name="w">记录所有物品重量的数组</param>///<param name="p">记录所有物品价格的数组</param>///<returns>放⼊背包的物品的最⼤价格</returns>public static int UpDown(int m,int i,int[] w,int[] p){//如果背包还能装的重量为0或者放⼊的物品编号为0,价格⾃然都是0if (i == 0 || m == 0) return0;//将第i个物品放⼊背包,但是物品放⼊后超重,这种情况下就不放⼊这个物品,看下⼀个物品是否能放⼊背包(物品编号-1)if(w[i] > m)return UpDown(m,i - 1, w, p);//在第i个物品能放⼊背包的情况下需要计算⽐较价格else{//计算并记录将物品放⼊背包的情况下的最⼤价格(递归物品编号-1,从背包剩余重量中扣除当前物品重量,由于当前物品放⼊背包,背包中物品价格加上当前物品价格)int maxValue1 = UpDown(m - w[i], i - 1, w, p) + p[i];//计算并记录不将物品放⼊背包的情况下的最⼤价格(递归物品编号-1,但是由于没有放⼊物品,背包剩余重量不变)int maxValue2 = UpDown(m, i - 1, w, p);//⽐较并取其中价格最⼤的那个if (maxValue1 > maxValue2)return maxValue1;return maxValue2;}}3.改进分治算法,使⽤数组记录已经运算过的问题结果//定义⼀个规则⼆维数组记录结果,⾏数对应背包重量,列数对应放⼊的物品个数,值对应这种情况背包中的最⼤重量public static int[,] result = new int[11, 4];static void Main(string[] args){int[] w = { 0, 3, 4, 5 };int[] p = { 0, 4, 5, 6 };Console.WriteLine(UpDown(9, w.Length - 1, w, p));Console.ReadKey();}public static int UpDown(int m,int i,int[] w,int[] p){if (i == 0 || m == 0) return0;//判断这种情况是不是已经计算过了,计算过了就直接返回if (result[m, i] != 0)return result[m, i];//在计算完成后需要将计算结果存储起来if(w[i] > m)return result[m,i] = UpDown(m,i - 1, w, p);else{int maxValue1 = UpDown(m - w[i], i - 1, w, p) + p[i];int maxValue2 = UpDown(m, i - 1, w, p);if (maxValue1 > maxValue2)return result[m, i] = maxValue1;return result[m, i] = maxValue2;}}4.动态规划算法(⾃下⽽上计算问题)static void Main(string[] args){int[] w = { 0, 3, 4, 5 };int[] p = { 0, 4, 5, 6 };Console.WriteLine(BottomUp(9, w.Length - 1, w, p));Console.ReadKey();}//记录运算结果的数组,⾏数代表背包重量,列数代表物品编号public static int[,] result = new int[11, 4];///<summary>///计算指定重量m的背包种放置前i个物品放置的最⼤价值///</summary>///<param name="m">背包重量</param>///<param name="i">物品编号</param>///<param name="w">记录所有物品重量的数组</param>///<param name="p">记录所有物品价格的数组</param>///<returns></returns>public static int BottomUp(int m,int i,int[] w,int[] p){//外层循环从1到m,背包重量从⼩到⼤for(int tempM = 1;tempM <= m; tempM++){//内层循环从1到i,物品个数从1到ifor(int tempI = 1;tempI <= i;tempI++){//如果已经计算过,不⽤再计算了if (result[tempM, tempI] != 0) continue;//如果当前物品重量超过背包剩余可装重量,不放⼊物品,将物品编号减⼀if (w[tempI] > tempM)result[tempM, tempI] = result[tempM, tempI - 1];//当前放置的物品重量不超过背包剩余可装重量,说明物品能放⼊背包//物品不⼀定放⼊背包最后的总价值最⼤,所以需要⽐较放⼊和不放⼊的情况得到的价格哪种更⼤else{int maxValue1 = result[tempM - w[tempI], tempI - 1] + p[tempI];int maxValue2 = result[tempM, tempI - 1];result[tempM, tempI] = maxValue1 > maxValue2 ? maxValue1 : maxValue2;}}}//⾃下⽽上计算完成后,返回重量为m的背包放⼊i个物品的最⼤价格即可return result[m, i];}。

动态规划法求01背包问题

动态规划法求01背包问题

动态规划法求01背包问题思路状态表⽰:f[i][j]表⽰前i个物品在容量为j的背包下的最⼤价值v[i]表⽰第i个物品的价值,w[i]表⽰第i个物品的重量状态转换:对于第i个物品如果当前背包不可以装下这个物品,那么当前的f[i][j] = f[i - 1][j],也就是上⼀个状态的最⼤价值如果当前背包可以装下这个物品,那么当前的f[i][j] = f[i - 1][j - v[i]] + w[i]和f[i - 1][j]取较⼤的那⼀个,第⼀个是考虑把第i个物品装⼊背包,那么背包物品的价值就是前i-1个物品装⼊容量为j-w[i]再加上第i个物品v[i]的价值,第⼆个是不把当前物品装⼊背包的价值,两个取⼤的那⼀个作为最优解代码详解:1. 0/1背包问题#include<iostream>using namespace std;const int N = 1e3 + 10;int f[N], w[N], v[N];int main(){int n, c;cin >> n >> c;for(int i = 1; i <= n; i ++ )cin >> v[i] >> w[i];for(int i = 1; i <= n; i ++)for(int j = c; j >= 0 && j >= v[i]; j --)f[j] = max(f[j], f[j - v[i]] + w[i]);cout << f[c] << endl;return 0;}}完全背包问题(每个物品可以使⽤⽆数次f[i][j] = max(f[i - 1][j], f[i][j - v[i]] + w[i])完全背包问题本来应该是在背包问题上再加⼀个循环的,但是可以推导出下⾯这个样⼦f[i][j] = max(f[i - 1][j], f[i][j - v[i]] + w[i])为什么呢,只是把0/1背包问题的f[i - 1][j - v[i] + w[i])的i-1换成了i就可以了?从头来说:完全背包问题,对于第i个物品,假设剩下的背包容量还可以装n个这个物品,那么⼀共就有n+1中决策⽅案,还有⼀种⽅案就是⼀个都不选那么对于第i个物品:它的最⼤价值就是f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]), f[i - 1][j - 2 * v[i]] + 2 * w[i]....)⼀直到n为⽌(①式)令 j = j - v[i] 那么f[i][j - v[i]] = max(f[i - 1][j - v[i]], f[i - 1][j - 2 * v[i]] + w[i].....)(②式)然后发现⼆式中的右边⽐较像⼀式中的第⼆项到最后⼀项,就是每⼀项都少了⼀个w[i]把⼆式带⼊⼀式,那么⼀式就是 f[i][j] = max(f[i - 1][j], f[i][j - v[i]] + w[i]), 就是下⾯这个状态⽅程啦 ;#include<iostream>using namespace std;const int N = 1e3 + 10;int f[N][N], w[N], v[N];int main(){int n, c;cin >> n >> c;for(int i = 1; i <= n; i ++)cin >> v[i] >> w[i];for(int i = 1; i <= n; i ++ ){for(int j = 1; j <= c; j ++ ){f[i][j] = f[i - 1][j];if(j >= v[i])f[i][j] = max(f[i - 1][j], f[i][j - v[i]] + w[i]);}}cout << f[n][c] << endl;return 0;}完全背包优化:#include<iostream>using namespace std;const int N = 1e3 + 10;int f[N], w[N], v[N];int main(){int n, c;cin >> n >> c;for(int i = 1; i <= n; i ++)cin >> v[i] >> w[i]; for(int i = 1; i <= n; i ++ )for(int j = v[i]; j <= c; j ++ )//从前往后更新 f[j] = max(f[j], f[j - v[i]] + w[i]);cout << f[c] << endl;return 0;}。

利用动态规划解决01背包问题01背包问题动态规划

利用动态规划解决01背包问题01背包问题动态规划

利用动态规划解决01背包问题01背包问题动态规划背包问题是一个经典的动态规划模型,很多关于算法的教材都把它作为一道例题,该问题既简单又容易理解,而且在某种程度上还能够揭示动态规划的本质。

将具有不同重量和价值的物体装入一个有固定载重量的背包,以获取最大价值,这类问题被称为背包问题。

背包问题可以扩展出很多种问题,而01背包问题是最常见、最有代表性的背包问题。

一、问题描述给定一个载重量为M的背包及n个物体,物体i的重量为wi、价值为pi,1≤i≤n,要求把这些物体装入背包,使背包内的物体价值总量最大。

此处我们讨论的物体是不可分割的,通常称这种物体不可分割的背包问题为01背包问题。

二、基本思路01背包问题的特点是:每种物体只有一件,可以选择放或者不放。

假设:xi表示物体i被装入背包的情况,xi=0,1。

当xi=0时,表示物体没有被装入背包;当xi=1时,表示物体被装入背包。

根据问题的要求,有如下的约束方程(1)和目标函数(2):三、利用动态规划法求解01背包问题(一)动态规划算法的基本思想动态规划算法通常用于求解具有某种最优性质的问题。

在这类问题中,可能会有许多可行解。

每一个解都对应于一个值,我们希望找到具有最优值的解。

动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。

与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。

若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算很多次。

如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。

我们可以用一个表来记录所有已解的子问题的答案。

不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中,这就是动态规划法的基本思路。

具体的动态规划算法多种多样,但它们具有相同的填表格式。

(二)算法设计假定背包的载重量范围为0~m。

01背包问题(动态规划法)

01背包问题(动态规划法)

0/1背包问题1. 问题描述给定一个载重量为m,n个物品,其重量为w i,价值为v i,1<=i<=n,要求:把物品装入背包,并使包内物品价值最大2. 问题分析在0/1背包问题中,物体或者被装入背包,或者不被装入背包,只有两种选择。

循环变量i,j意义:前i个物品能够装入载重量为j的背包中(n+1)*(m+1)数组value意义:value[i][j]表示前i个物品能装入载重量为j的背包中物品的最大价值若w[i]>j,第i个物品不装入背包否则,若w[i]<=j且第i个物品装入背包后的价值>value[i-1][j],则记录当前最大价值(替换为第i个物品装入背包后的价值)计算最大价值的动态规划算法如下://计算for(i=1;i<row;i++){for(j=1;j<col;j++){//w[i]>j,第i个物品不装入背包value[i][j]=value[i-1][j];//w[i]<=j,且第i个物品装入背包后的价值>value[i-1][j],则记录当前最大价值int temp=value[i-1][j-w[i]]+v[i];if(w[i]<=j && temp>value[i][j])value[i][j]=temp;}}即该段程序完成以下n个阶段:1:只装入1个物品,确定在各种不同载重量的背包下,能够得到的最大价值2:装入2个物品,确定在各种不同载重量的背包下,能够得到的最大价值。

n:以此类推,装入n个物品,确定在各种不同载重量的背包下,能够得到的最大价值3. 问题求解确定装入背包的具体物品,从value[n][m]向前逆推:若value[n][m]>value[n-1][m],则第n个物品被装入背包,且前n-1个物品被装入载重量为m-w[n]的背包中否则,第n个物品没有装入背包,且前n-1个物品被装入载重量为m的背包中以此类推,直到确定第一个物品是否被装入背包为止。

蛮力法、动态规划法、回溯法和分支限界法求解01背包问题【精选】

蛮力法、动态规划法、回溯法和分支限界法求解01背包问题【精选】

一、实验内容:分别用蛮力法、动态规划法、回溯法和分支限界法求解0/1背包问题。

注:0/1背包问题:给定种物品和一个容量为的背包,物品的重n C i 量是,其价值为,背包问题是如何使选择装入背包内的物品,使得装i w i v 入背包中的物品的总价值最大。

其中,每种物品只有全部装入背包或不装入背包两种选择。

二、所用算法的基本思想及复杂度分析:1.蛮力法求解0/1背包问题:1)基本思想:对于有n 种可选物品的0/1背包问题,其解空间由长度为n 的0-1向量组成,可用子集数表示。

在搜索解空间树时,深度优先遍历,搜索每一个结点,无论是否可能产生最优解,都遍历至叶子结点,记录每次得到的装入总价值,然后记录遍历过的最大价值。

2)代码:#include<iostream>#include<algorithm>using namespace std;#define N 100//最多可能物体数struct goods //物品结构体{int sign;//物品序号int w;//物品重量int p;//物品价值}a[N];bool m(goods a,goods b){return (a.p/a.w)>(b.p/b.w);}int max(int a,int b){return a<b?b:a;}int n,C,bestP=0,cp=0,cw=0;int X[N],cx[N];/*蛮力法求解0/1背包问题*/int Force(int i){if(i>n-1){if(bestP<cp&&cw+a[i].w<=C){for (int k=0;k<n;k++)X[k]=cx[k];//存储最优路径bestP=cp;}return bestP;}cw=cw+a[i].w;cp=cp+a[i].p;cx[i]=1;//装入背包Force(i+1);cw=cw-a[i].w;cp=cp-a[i].p;cx[i]=0;//不装入背包Force(i+1);return bestP;}int KnapSack1(int n,goods a[],int C,int x[]){Force(0);return bestP;}int main(){goods b[N];printf("物品种数n: ");scanf("%d",&n);//输入物品种数printf("背包容量C: ");scanf("%d",&C);//输入背包容量for (int i=0;i<n;i++)//输入物品i 的重量w 及其价值v {printf("物品%d 的重量w[%d]及其价值v[%d]:",i+1,i+1,i+1);scanf("%d%d",&a[i].w,&a[i].p);b[i]=a[i];}int sum1=KnapSack1(n,a,C,X);//调用蛮力法求0/1背包问题printf("蛮力法求解0/1背包问题:\nX=[ ");for(i=0;i<n;i++)cout<<X[i]<<" ";//输出所求X[n]矩阵printf("]装入总价值%d\n",sum1);bestP=0,cp=0,cw=0;//恢复初始化}3)复杂度分析:蛮力法求解0/1背包问题的时间复杂度为:。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
for(i=0;i<10;i++)
for(j=0;j<15;j++)
{
printf("%d ",c[i][j]);
if(j==14)printf("\n");
}
system("pause");
}
-----------------------------------------------------------------------------------------------------------------------------------题目
过程ZeroOnePack,表示处理一件01背包中的物品,两个参数cost、weight分别表明这件物品的费用和价值。
procedure ZeroOnePack(cost,weight)
for v=V..cost
f[v]=max{f[v],f[v-cost]+weight}
注意这个过程里的处理与前面给出的伪代码有所不同。前面的示例程序写成v=V..0是为了在程序中体现每个状态都按照方程求解了,避免不必要的思维复杂度。而这里既然已经抽象成看作黑箱的过程了,就可以加入优化。费用为cost的物品不会影响状态f[0..cost-1],这是显然的。
有了这个过程以后,01背包问题的伪代码就可以这样写:
for i=1..N
ZeroOnePack(c[i],w[i]);
初始化的细节问题
我们看到的求最优解的背包问题题目中,事实上有两种不太相同的问法。有的题目要求“恰好装满背包”时的最优解,有的题目则并没有要求必须把背包装满。
一种区别这两种问法的实现方法是在初始化的时候有所不同。
f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}
这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的。
所以有必要将它详细解释一下:“将前i件物品放入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为v的背包中”,价值为f[i-1][v];如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-c[i]的背包中”,此时能获得的最大价值就是f[i-1][v-c[i]]再加上通过放入第i件物品获得的价值w[i]。
总的最佳方案是5+4为9.这样.一排一排推下去。最右下放的数据就是最大的价值了。(注意第3排的背包容量为7的时候,最佳方案不是本身的6.而是上一排的9.说明这时候3号物品没有被选.选的是1,2号物品.所以得9.)
从以上最大价值的构造过程中可以看出。
f(n,m)=max{f(n-1,m), f(n-1,m-w[n])+P(n,m)}这就是书本上写的动态规划方程.这回清楚了吗?
for(j=0;j<100;j++)
c[i][j]=0;
for(i=1;i<n+1;i++)
for(j=1;j<m+
{
if(p[i]+c[i-1][j-w[i]]>c[i-1][j])
c[i][j]=p[i]+c[i-1][j-w[i]];
else
c[i][j]=c[i-1][j];
有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。
求解将哪些物品装入背包可使价值总和最大。
基本思路
这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。
用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便是:
优化空间复杂度
以上方法的时间和空间复杂度均为O(VN),其中时间复杂度应该已经不能再优化了,但空间复杂度却可以优化到O。
先考虑上面讲的基本思路如何实现,肯定是有一个主循环i=1..N,每次算出来二维数组f[i][0..V]的所有值。那么,如果只用一个数组f[0..V],能不能保证第i次循环结束后f[v]中表示的就是我们定义的状态f[i][v]呢?f[i][v]是由f[i-1][v]和f[i-1][v-c[i]]两个子问题递推而来,能否保证在推f[i][v]时(也即在第i次主循环中推f[v]时)能够得到f[i-1][v]和f[i-1][v-c[i]]的值呢?事实上,这要求在每次主循环中我们以v=V..0的顺序推f[v],这样才能保证推f[v]时f[v-c[i]]保存的是状态f[i-1][v-c[i]]的值。伪代码如下:
如果是第一种问法,要求恰好装满背包,那么在初始化时除了f[0]为0其它f[1..V]均设为-∞,这样就可以保证最终得到的f[N]是一种恰好装满背包的最优解。
如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将f[0..V]全部设为0。
为什么呢?可以这样理解:初始化的f数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为0的背包可能被价值为0的nothing“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是-∞了。如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。
动态规划是用空间换时间的一种方法的抽象。其关键是发现子问题和记录其结果。然后利用这些结果减轻运算量。
比如01背包问题。
因为背包最大容量M未知。所以,我们的程序要从1到M一个一个的试。比如,开始任选N件物品的一个。看对应M的背包,能不能放进去,如果能放进去,并且还有多的空间,则,多出来的空间里能放N-1物品中的最大价值。怎么能保证总选择是最大价值呢?看下表。
for i=1..N
for v=V..0
可以改成
for i=1..n
bound=max{V-sum{w[i..n]},c[i]}
for v=V..bound
这对于V比较大时是有用的。
小结
01背包问题是最基本的背包问题,它包含了背包问题中设计状态、方程的最基本思想,另外,别的类型的背包问题往往也可以转换成01背包问题求解。故一定要仔细体会上面基本思路的得出方法,状态转移方程的意义,以及最后怎样优化的空间复杂度。
这个小技巧完全可以推广到其它类型的背包问题,后面也就不再对进行状态转移之前的初始化进行讲解。
一个常数优化
前面的伪代码中有for v=V..1,可以将这个循环的下限进行改进。
由于只需要最后f[v]的值,倒推前一个物品,其实只要知道f[v-w[n]]即可。以此类推,对以第j个背包,其实只需要知道到f[v-sum{w[j..n]}]即可,即代码中的
下面是实际程序:
#include<stdio.h>
int c[10][100];
int knapsack(int m,int n)
{
int i,j,w[10],p[10];
for(i=1;i<n+1;i++)
scanf("\n%d,%d",&w[i],&p[i]);
for(i=0;i<10;i++)
测试数据:
10,3
3,4
4,5
5,6
c[i][j]数组保存了1,2,3号物品依次选择后的最大价值.
这个最大价值是怎么得来的呢?从背包容量为0开始,1号物品先试,0,1,2,的容量都不能放.所以置0,背包容量为3则里面放4.这样,这一排背包容量为4,5,6,....10的时候,最佳方案都是放4.假如1号物品放入背包.则再看2号物品.当背包容量为3的时候,最佳方案还是上一排的最价方案c为4.而背包容量为5的时候,则最佳方案为自己的重量5.背包容量为7的时候,很显然是5加上一个值了。加谁??很显然是7-4=3的时候.上一排c3的最佳方案是4.所以。
for i=1..N
for v=V..0
f[v]=max{f[v],f[v-c[i]]+w[i]};
其中的f[v]=max{f[v],f[v-c[i]]}一句恰就相当于我们的转移方程
f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]},因为现在的f[v-c[i]]就相当于原来的f[i-1][v-c[i]]。如果将v的循环顺序从上面的逆序改成顺序的话,那么则成了f[i][v]由f[i][v-c[i]]推知,与本题意不符,但它却是另一个重要的背包问题P02最简捷的解决方案,故学习只用一维数组解01背包问题是十分必要的。事实上,使用一维数组解01背包的程序在后面会被多次用到,所以这里抽象出一个处理一件01背包中的物品过程,以后的代码中直接调用不加说明。
}
else c[i][j]=c[i-1][j];
}
return(c[n][m]);
}
int main()
{
int m,n;int i,j;
scanf("%d,%d",&m,&n);
printf("Input each one:\n");
printf("%d",knapsack(m,n));
printf("\n");
相关文档
最新文档