问题描述:有若干个物品,每个物品具有两个属性,分别是重量(w),价值(v),现在有一个容量为G的背包,挑选物品放入背包中达到物品价值最大值,并输出相应的物品价值或者物品重量。
例:w={0,2,3,4,5},v={0,3,4,5,6} , G = 8
将物品放入背包的过程中,放入每一件物品的过程都会后阶段的放入产生影响,这是一个阶段性的过程,且每一件物品的放入都是容量更小的背包的一个解,既原问题的子问题,把这些解记录下并整合将得到原问题的最优解。
所以该问题可以通过动态规划来解决,既然通过动态规划的话,那么就需要构造一个数组表来记录各个阶段的状态。
在这里,设dp[i,j] 表示背包的总价值,那么i,j所代表的含义就需要考量。在这问题中,有一个关键的属性:背包容量,所以背包容量就可以代替i,j其中一个变量。另外一个无疑是物品的数量作为数组的变量。
那么现在规定:dp[i,j] = 背包价值 其中 i = 第i件物品,j = 背包剩余容量,w[i] = 第 i 件物品的重量,v[i] = 第 i 件物品的价值
现在的问题在于第 i 件物品到底装不装入背包,那么可以得到状态转移方程:
dp[i,j] = 1, dp[i-1][j] w[i] > j(不装入背包)
2, max( dp[i-1,j] , dp[i-1,j-w[i]]+w[i]) 装入背包
1,装入与不装入背包的条件在于够不够空间装入
2,如果空间足够,可以选择装入背包,那么就要注意这件物品装入背包的价值到底是不是最有价值的东西即该物品装入了但是并没有不装入该物品价值来的高,因为你既浪费了空间又得不到最大的价值,此时就要比较装入该物品和不装入该物品时的大小
下面是全文代码:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace 背包问题_1_ 8 { 9 class Program 10 { 11 public static int G = 8; // 容量 12 public static int []w = { 0, 2, 3, 4, 5, }; // 物品重量 13 public static int []v = { 0, 3, 4, 5, 6 }; // 价值 14 public static int[,] dp = new int[w.Length , G+1]; 15 public static int[,] fdp = new int[w.Length, G + 1]; 16 static void Main(string[] args) 17 { 18 DP(); 19 for(int i=0;i<w.Length;++i) 20 { 21 for(int j=0;j<=G;++j) 22 { 23 Console.Write(dp[i, j]); 24 } 25 Console.Write("\n"); 26 } 27 findDP(4,8); 28 Console.ReadKey(); 29 } 30 31 public static void findDP(int i,int j) 32 { 33 if (i == 0 || j == 0) return; 34 else if (fdp[i,j] == 1) 35 { 36 findDP(i - 1, j - w[i]); 37 Console.WriteLine(v[i]); 38 } 39 else 40 { 41 findDP(i - 1, j); 42 } 43 } 44 45 public static int [,] DP() 46 { 47 for (int i = 1;i<w.Length;++i) 48 { 49 for(int j = 1;j<=G;j++) 50 { 51 if (i == 0 || j == 0) 52 dp[i, j] = 0; 53 else if (j<w[i]) 54 { 55 dp[i, j] = dp[i - 1, j]; 56 } 57 else 58 { 59 dp[i, j] = Math.Max(dp[i - 1, j], dp[i - 1, j - w[i]] + v[i]); 60 if (dp[i - 1, j] <= dp[i - 1, j - w[i]] + v[i]) 61 { 62 fdp[i, j] = 1; 63 } 64 } 65 } 66 } 67 return dp; 68 } 69 } 70 }
完成填表过程的是:
1 public static int [,] DP() 2 { 3 for (int i = 1;i<w.Length;++i) 4 { 5 for(int j = 1;j<=G;j++) 6 { 7 if (i == 0 || j == 0) 8 dp[i, j] = 0; 9 else if (j<w[i]) 10 { 11 dp[i, j] = dp[i - 1, j]; 12 } 13 else 14 { 15 dp[i, j] = Math.Max(dp[i - 1, j], dp[i - 1, j - w[i]] + v[i]); 16 if (dp[i - 1, j] <= dp[i - 1, j - w[i]] + v[i]) 17 { 18 fdp[i, j] = 1; 19 } 20 } 21 } 22 } 23 return dp; 24 }
其中dp[]是负责记录过程,fdp[]是负责记录装入背包的物品
输出装入物品的价值的片段:
1 public static void findDP(int i,int j) 2 { 3 if (i == 0 || j == 0) return; 4 else if (fdp[i,j] == 1) 5 { 6 findDP(i - 1, j - w[i]); 7 Console.WriteLine(v[i]); 8 } 9 else 10 { 11 findDP(i - 1, j); 12 } 13 }
同样的,和最长子序列和最长子串一样,从当前状态回溯回上一个状态。