动态规划是一种高效的算法。在数学和计算机科学中,是一种将复杂问题的分成多个简单的小问题思想 ---- 分而治之。因此我们使用动态规划的时候,原问题必须是重叠的子问题。运用动态规划设计的算法比一般朴素算法高效很多,因为动态规划不会重复计算已经计算过的子问题。因为动态规划又可以称为“记忆化搜索”。
01背包是介绍动态规划最经典的例子,同时也是最简单的一个。我们先看看01背包的是什么?
问题(01背包): 有n个重量和价值分别为Wi和Vi的物品。从这些物品中挑出总重量不超过W的物品,求所有挑选方案中价值总和的最大值。这就是被称为01背包的问题。在没学习动态规划之前,我们看到这个问题第一反应会用dfs搜索一遍。那我们先使用这种方法来求解01背包问题:
//n,W 如题意所述 int W, n; //w[i]和v[i]分别表示Wi,Vi int w[MAXN], v[MAXN]; //从第i个物品开始挑选总重量小于j的部分 int dfs(int i, int j){ int res; //已经没有剩余物品 if(i == n) res = 0; //无法挑选第i个物品 else if(j < w[i]) res = dfs(i+1, j); //比较挑和不挑的情况,选取最大的情况 else res = max(dfs(i+1, j), dfs(i+1, j-w[i])+v[i]); return res; }
//n,W 如题意所述 int W, n; //w[i]和v[i]分别表示Wi,Vi int w[MAXN], v[MAXN]; //保存每一次搜索的答案 //初始化dp数组的值,使其全为-1 int dp[MAXN][MAXN]; //从第i个物品开始挑选总重量小于j的部分 int dfs(int i, int j){ if(dp[i][j] >= 0) return dp[i][j]; int res; //已经没有剩余物品 if(i == n) res = 0; //无法挑选第i个物品 else if(j < w[i]) res = dfs(i+1, j); //比较挑和不挑的情况,选取最大的情况 else res = max(dfs(i+1, j), dfs(i+1, j-w[i])+v[i]); return res; }
仔细分析,可以发现我们还可以有更简单的写法:
//dp[i+1][j] 表示从前i个物品挑选出总重量超过j的物品时,背包中的最大价值 void solve(){ //还没开始挑选的时候,背包里的总价值为0 for(int j = 0; j <= W; j++) dp[0][j] = 0; for(int i = 0; i < n; i++){ for(int j = 0; j <= W; j++){ if(j < w[i]) dp[i+1][j] = dp[i][j]; else dp[i+1][j] = max(dp[i+1], dp[j-w[i]]+v[i]); } } }