标签:pre 空间 bsp 硬币 ring 表达式 else [] 含义
动态规划基本思路:
0. 应用场景:1. 后一状态依赖前一状态的值;2. 无后效性,即后面的阶段不会对前一阶段有影响
1. 找到状态转移方程,从后往前推dp[i]=f(dp[i-1]),即当前值需要前一阶段的值作为基础
2. 写程序时,根据状态转移方程,从dp[1]用for循环从前往后计算,dp[1]的前一阶段是dp[0],要以要先设置初始值,即dp[0]=0
分析:
我们先假设一个函数 d(i) 来表示需要凑出 i 的总价值需要的最少硬币数量。
可以看出,除了第 1 步这个看似基本的公理外,其他往后的结果都是建立在它之前得到的某一步的最优解上,加上 1 个硬币得到。得出:
状态转化方程:dp[i]=Math.min(dp[i], dp[i-coin[j]]+1)
1 public static void main(String[] args) { 2 3 int[] coins={1,3,5}; 4 int money=12; 5 System.out.println(CalculateCoin(money,coins)); 6 } 7 8 /* 9 * dp[i]表示money=i时,需要的最小硬币数量 10 * 状态转化方程:dp[i]=Math.min(dp[i], dp[i-coin[j]]+1) 11 */ 12 public static int CalculateCoin(int k,int[] coins){ 13 14 int[] dp=new int[k+1]; 15 dp[0]=0; 16 17 for(int i=1;i<dp.length;i++){ 18 dp[i]=Integer.MAX_VALUE; 19 for(int j=0;j<coins.length;j++){ 20 if(coins[j]<=i){ 21 dp[i]=Math.min(dp[i], dp[i-coins[j]]+1); 22 } 23 } 24 } 25 26 return dp[k]; 27 }
分析:
先将原始问题一般化,欲求背包能够获得的总价值,即欲求前i个物体放入容量为m(kg)背包的最大价值dp[i][m]——使用一个数组来存储最大价值,当m取10,i取3时,即原始问题了。而前i个物体放入容量为m的背包,又可以转化成前(i-1)个物体放入背包的问题。下面使用数学表达式描述它们两者之间的具体关系。
表达式中各个符号的具体含义。
w[i] : 第i个物体的重量;
v[i] : 第i个物体的价值;
d[i][j] :前i个物体放入容量为j的背包的最大价值;
对物体个数及背包重量进行递推,列出一个表格,当逐步推出表中每个值的大小,那个最大价值就求出来了。
推导过程中,要横向计算:
先推导i=1,即只放入第一件物品时,容量为j的背包的最大价值;
再推导i=2,即放入第二件物品时,容量为j的背包的最大价值;
再推导i=3,即放入第三件物品时,容量为j的背包的最大价值
状态转移方程:
1. 如果物品可以放入背包,即w[i]<=j,dp[i][j]=Math.max(dp[i][j-1], dp[i-1][j-w[i]]+v[i])
解释:dp[i-1][j-w[i]]+v[i] 表示第i件物品物品放入背包的价值,即(放入前一个物品(i-1) 背包容量为j-w[i]时的价值)+v[i]
然后再和dp[i][j-1]进行比较,即放入第i件物品背包容量为j-1时的价值,取其大者。
2. 如果物品重量超过背包容量,即w[i]>j,dp[i][j]=dp[i][j-1]。
1 public static void main(String[] args) { 2 int[] w = {3,4,5}; //物品重量 3 int[] v = {4,4,6}; //物品价值 4 int m=10; 5 int n=w.length; 6 System.out.println(MaxValue(w,v,m,n)); 7 System.out.println(MaxValue1(w,v,m,n)); 8 System.out.println(MaxValue2(w,v,m,n)); 9 10 } 11 12 /* 13 * 01背包问题 14 * 15 * 状态转移方程: 16 * 1. 如果物品可以放入,即w[i]<=j: 17 * dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]) 18 * 19 * 2. 物品无法放入背包,即w[i]>j: 20 * dp[i][j]=dp[i-1][j] 21 * 22 */ 23 public static int MaxValue(int[] w, int[] v, int m, int n){ 24 int[][] dp=new int[n+1][m+1]; 25 for(int i=0;i<dp.length;i++){ 26 dp[i][0]=0; 27 } 28 29 for(int j=0;j<dp[0].length;j++){ 30 dp[0][j]=0; 31 } 32 33 for(int i=1;i<dp.length;i++){ 34 for(int j=1;j<dp[0].length;j++){ 35 if(w[i-1]<=j) 36 dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-w[i-1]]+v[i-1]); 37 else 38 dp[i][j]=dp[i-1][j]; 39 } 40 } 41 42 return dp[n][m]; 43 } 44 45 /* 46 * 01背包内存优化 47 * 因为当前阶段值dp值,只依赖于上一阶段的dp值 48 * 所以可以用一位数组,内圈for循环从后往前计算即可 49 */ 50 public static int MaxValue1(int[] w,int[] v,int m,int n){ 51 52 int[] dp=new int[m+1]; 53 54 for(int i=0;i<dp.length;i++) 55 dp[i]=0; 56 57 for(int i=0;i<n;i++){ 58 for(int j=dp.length-1;j>=w[i];j--){ 59 dp[j]=Math.max(dp[j],dp[j-w[i]]+v[i]); 60 } 61 } 62 63 return dp[m]; 64 } 65 66 /* 67 * 完全背包问题,即物品可以重复使用 68 * 把01背包的内存优化版,内圈for循环从前往后计算即可 69 */ 70 public static int MaxValue2(int[] w,int[] v,int m,int n){ 71 72 int[] dp=new int[m+1]; 73 74 for(int i=0;i<dp.length;i++) 75 dp[i]=0; 76 77 for(int i=0;i<n;i++){ 78 for(int j=w[i];j<dp.length;j++){ 79 dp[j]=Math.max(dp[j],dp[j-w[i]]+v[i]); 80 81 } 82 } 83 84 return dp[m]; 85 } 86
内存优化:
每一次dp[i][j]改变的值只与dp[i-1][j]或dp[i][j-1]有关,dp[i-1][j]是前一次i循环保存下来的值。因此,可以将dp缩减成一维数组,从而达到优化空间的目的,状态转移方程转换为 dp[j]= max{ dp[j], dp[j-w(i)]+v[i] }
并且,状态转移方程,每一次推导dp[i][j]是通过dp[i-1][j-w(i)]来推导的,所以一维数组中j的扫描顺序应该从大到小,否者前一次循环保存下来的值将会被修改,从而造成错误。
分析:把01背包的内存优化版,内圈for循环从前往后计算即可
编辑距离,是指两个字串之间,由一个字符串转成另一个字符串所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。
分析:
假设word1和word2分别为:michaelab和michaelxy,dis[i][j]作为word1和word2之间的编辑距离,即word1[i]和word2[j]的编辑距离。
dis[0][0]表示word1和word2都为空的时候,此时他们的编辑距离为0。
dis[0][j]就是word1为空,word2长度为j的情况,此时他们的编辑距离为j,也就是从空,添加j个字符转换成word2的最小编辑距离为j;同理dis[i][0]就是,word1长度为i,word2为空时,word1需要删除i个字符才能转换成空,所以转换成word2的最小编辑距离为i。
状态转移方程:
1. 如果word1.charAt(i)==word2.charAt(j):
dp[i][j]=dp[i-1][j-1]
2. 如果char1!=char2:
insert=1+dp[i][j-1];
delete=1+dp[i-1][j];
replace=1+dp[i-1][j-1];
dp[i][j]=Math.min(Math.min(insert,delete), replace);
假设word1[i]和word2[j] (此处i = j) 分别为:michaelab和michaelxy
显然如果word1.charAt(i)==word2.charAt(j), 那么dis[i][j] = dis[i-1][j-1]。
如果word1.charAt(i)!=word2.charAt(j),那么:
添加:也就是在michaelab后面添加一个y,那么word1就变成了michaelaby,此时
dis[i][j] = 1 + dis[i][j-1];
上式中,1代表刚刚的添加操作,添加操作后,word1变成michaelaby,word2为michaelxy。dis[i][j-1]代表从word[i]转换成word[j-1]的最小它是指word1[i]和word2[j]的编辑距离,也就是michaelab转换成michaelx的最小它是指word1[i]和word2[j]的编辑距离,由于两个字符串尾部的y==y,所以只需要将michaelab变成michaelx就可以了,而他们之间的最小它是指word1[i]和word2[j]的编辑距离就是dis[i][j-1]。
删除:也就是将michaelab后面的b删除,那么word1就变成了michaela,此时
dis[i][j] = 1 + dis[i-1][j];
上式中,1代表刚刚的删除操作,删除操作后,word1变成michaela,word2为michaelxy。dis[i-1][j]代表从word[i-1]转换成word[j]的最小它是指word1[i]和word2[j]的编辑距离,也就是michaela转换成michaelxy的最小它是指word1[i]和word2[j]的编辑距离,所以只需要将michaela变成michaelxy就可以了,而他们之间的最小它是指word1[i]和word2[j]的编辑距离就是dis[i-1][j]。
替换:也就是将michaelab后面的b替换成y,那么word1就变成了michaelay,此时
dis[i][j] = 1 + dis[i-1][j-1];
上式中,1代表刚刚的替换操作,替换操作后,word1变成michaelay,word2为michaelxy。dis[i-1][j-1]代表从word[i-1]转换成word[j-1]的最小它是指word1[i]和word2[j]的编辑距离,也即是michaelay转换成michaelxy的最小它是指word1[i]和word2[j]的编辑距离,由于两个字符串尾部的y==y,所以只需要将michaela变成michaelx就可以了,而他们之间的最小它是指word1[i]和word2[j]的编辑距离就是dis[i-1][j-1]。
标签:pre 空间 bsp 硬币 ring 表达式 else [] 含义
原文地址:http://www.cnblogs.com/ze7777/p/7498059.html