动态规划方法--0-1背包问题&最长公共子串&最长公共子序列
一、动态规划方法—基本概念
1、解决多阶段决策过程最优化的一种数学方法
2、1951年美国数学家贝尔曼等人根据多阶段决策问题的特点,把多阶段决策问题变换为一系列互相联系的单阶段问题,然后逐个加以解决,即提出了动态规划这个方法
3、基本概念:阶段、状态、决策、策略、状态转移方程、指标函数和最优值函数
4、基本思想:从边界条件开始,逐段递推寻优,在每一个子问题的求解中,均利用了它前面的子问题的最优化结果,依次进行,最后一个子问题所得的最优解,就是整个问题的最优解
5、基本方程式—难点:从实际问题中求得基本方程式,即抽象出动态规划表dp,dp一般是一个数组,可能是一维的也可能是二维的,也可能是其他的数据结构,以空间换取时间的算法
6、由于初始状态是已知的,每阶段的决策都是该段状态的函数,故最优策略所经过的各阶段状态便可逐次变换得到,从而确定最优路线
7、最优化原理,也就是最优子结构性质。这指的是一个最优化策略具有这样的性质,无论过去状态和决策如何,对前面的决策所形成的状态而言,余下的决策必须构成最优策略,简单来说就是一个最优化策略的子策略总是最优的,如果一个问题满足最优化原理,就称其有最优子结构性质。
二、0-1背包问题
问:一个背包承重Wkg,有n(1,2......n)件物品。已知第件物品的重量为kg,价值是,每件物品只能选择要装入还是不装入背包,要求在不超过背包承重的前提下,选出的物品总价值最大
分析:假设表示第x件物品,不超过重量y的时候的最大价值,则第x件物品的情况:
情况1:如果选择了第x件物品,则前x-1件物品的重量不能超过,此时
情况2:如果不选择第x件物品,此时
综上所述:
代码如下:
int bag_0_1() { int n=5;//物品个数 int cap = 10;//最大承重 int v[5] = {5,18,23,22,8};//每个物品的价值 int w[5] = {4,3,7,6,1};//每个物品的重量 int i=0, j=0; //dp[n+1][cap+1] int **dp = new int*[n+1]; for(i = 0; i < n+1; i++) { dp[i] = new int[cap+1]; } for(i = 0; i < n+1; i++) { for(j = 0; j < cap+1; j++) { dp[i][j]=0;//第0行都初始化为0 } } //============================================================================== for (i=1; i <=n; i++)/*枚举物品*/ { for (j=1; j<cap+1; j++)/*枚举重量*/ { //判断枚举的重量和当前选择的物品重量的关系 //如果枚举的和总量大于等于选择物品,则需要判断是否选择当前物品 if (j-w[i-1]>=0) { dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i-1]]+v[i-1]); } else { /*如果枚举的重量还没有当前选择物品的重量大,那就只能是不取当前物品*/ dp[i][j] = dp[i-1][j]; } } } //============================================================= printf("dp[%d][%d]=%d\n", n, cap, dp[n][cap]); for(j=0; j<cap+1; ++j) { if(j==0) printf("\t "); printf("%d\t", j); } printf("\n"); for(i=0; i<n+1; ++i) { if(i==0) printf("\t %d ", i); if(i>0) { printf("w[%d]=%d(%d) %d ", i-1, w[i-1], v[i-1], i); } for(j=0; j<cap+1; ++j) { //printf("dp[%d][%d]=%d\t", i, j, dp[i][j]); //if(j==0) printf("\t"); printf("%d\t", dp[i][j]); } printf("\n"); } printf("\n"); //============================================================= //逆序倒推求得命中的数据 i=n; j=cap; while(i>=1 && j>=1 /*&& j-w[i-1]>=0*/) { if(j-w[i-1]>=0 && dp[i][j] == dp[i-1][j-w[i-1]]+v[i-1] ) { printf("hit info: w[%d]=%d, v[%d]=%d\n", i-1, w[i-1], i-1, v[i-1]); j=j-w[i-1]; i=i-1; } else if(dp[i][j]==dp[i-1][j] || j-w[i-1]<0) { i--; } else if(dp[i][j] > dp[i-1][j]) { j--; } } for(i = 0; i < n+1; i++) //释放动态申请的二维数组 { delete[] dp[i]; } delete[] dp; return -1; }
运行结果如下:
三、最长公共子序列
字符串或者序列串的一个子序列是指,其中
如:字符串abcdef,其中 acf、bd等都是子序列
问:,,求,的最长公共子序列??
设:表示和的一个最长公共子序列
情况1:,则
情况2:,则
设:记录序列的字符串长度
综上所述,公式如下:
代码如下:
1 int longest_common_subsequence(std::vector<std::string> vMainStr, std::vector<std::string> vSubStr) 2 { 3 //动态规划算法 4 int i,j,k,len1,len2,max,x,y; 5 6 len1 = vMainStr.size(); 7 len2 = vSubStr.size(); 8 9 printf("len1=%d, len2=%d\n", len1, len2); 10 11 int **dp = new int*[len1+1]; 12 for(i = 0; i < len1+1; i++) 13 { 14 dp[i] = new int[len2+1]; 15 } 16 17 for(i = 0; i < len1+1; i++) 18 { 19 dp[i][0]=0;//第0列都初始化为0 20 } 21 22 for(j = 0; j < len2+1; j++) 23 { 24 dp[0][j]=0;//第0行都初始化为0 25 } 26 27 28 max = -1; 29 //求dp值 30 for(i = 1; i < len1+1; i++)//str1 31 { 32 for(j = 1; j < len2+1; j++)//str2 33 { 34 //subsequence 35 if(vMainStr[i-1]==vSubStr[j-1]) 36 { 37 dp[i][j]=dp[i-1][j-1]+1; 38 } 39 else if(dp[i][j-1] > dp[i-1][j]) 40 { 41 dp[i][j]=dp[i][j-1]; 42 } 43 else 44 { 45 dp[i][j]=dp[i-1][j]; 46 } 47 } 48 } 49 50 //==================================================================================================== 51 //输出dp数组情况 52 printf("\n\n"); 53 int q=0, p=0; 54 for(q=0; q<len2; ++q) 55 { 56 if(q==0) printf(" \t"); 57 58 printf("%s\t", vSubStr[q].c_str()); 59 } 60 printf("\n"); 61 62 for(q=0; q<len1+1; ++q) 63 { 64 if(q==0) printf(" "); 65 if(q>0) printf("%s ",vMainStr[q-1].c_str()); 66 for(p=0; p<len2+1; ++p) 67 { 68 //printf("dp[%d][%d]=%d\t", q, p, dp[q][p]); 69 printf("%d\t", dp[q][p]); 70 } 71 printf("\n"); 72 } 73 printf("\n\n"); 74 //==================================================================================================== 75 76 //输出公共子序列 77 std::string s,sTmp; 78 i = len1; 79 j = len2; 80 k = dp[i][j]; 81 max = k; 82 printf("i=%d, j=%d, k=%d\n", i, j, k); 83 84 while(i>0 & j>0) 85 { 86 if(vMainStr[i-1] == vSubStr[j-1] && dp[i][j]==dp[i-1][j-1]+1) 87 { 88 sTmp = vMainStr[i-1]; 89 sTmp.append(s); 90 s = sTmp; 91 --j; 92 --i; 93 }else if(vMainStr[i-1] != vSubStr[j-1] && dp[i-1][j] > dp[i][j-1])//上>左 94 { 95 --i; 96 } 97 else 98 { 99 --j; 100 } 101 } 102 103 printf("Longest-Common-subsequence:%s\n", s.c_str()); 104 105 for(i = 0; i < len1+1; i++) //释放动态申请的二维数组 106 { 107 delete[] dp[i]; 108 } 109 110 delete[] dp; 111 112 return max; 113 }
运行结果: