**
半小时掌握基础动态规划算法
**
零、什么是动态规划?
这里就直接链接这位大佬的解释,讲的是真的好,我愿称之为最强!
https://www.zhihu.com/question/23995189
下面是几种例题:
1.最短路程
2.路径种类
3.背包问题
4.爬楼梯问题
5.字符串问题**
一、最短路程问题
(一)题目:如下给出制定3x3数组,约定第一行第一列为起点,第三行第三列为终点,问起点到终点最短路程为多少?(数组值为路程长度)
a[3][3]={
0,5,4,
6,2,5,
3,4,0
}
(二)解题思路:这是一题基础中基础的求最短路程问题,可以使用bfs解答,这里也可以使用动态规划,分三种情况讨论即可。
(三)解题代码
#include<bits/stdc++.h> //万能头文件,但是会耗时
using namespace std;
int a[3][3]={
0,5,4,
6,2,5,
3,4,0
};
int main(){
int dp[4][4]={0};
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
//动态规划解类似地图题目考虑边界
if(i==0&&j==0){
continue;
}
if(i==0){
dp[i][j]=dp[i][j-1]+a[i][j];
}
if(j==0){
dp[i][j]=dp[i-1][j]+a[i][j];
}
else{
//计算内部的路程值
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+a[i][j];
}
}
}
printf("%d",dp[2][2]);//最后打印的是n-1行和列
}
二、路径方案总数
(一)题目:n行m列的点阵行列,从第一行第一列出发,只能从右边和下边走,偶数行和列不能走,问走到第n行第m列有多少种方案。
(二)基础动态规划题(源于某比赛题)值得注意的是偶数列行不能走,添加限制条件即可。基本思路和上面一样。(解类似地图型的动态规划题需要考虑边界值)
(三)解题代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,m;
scanf("%d%d",&n,&m);
int dp[100][100]={0};
for(int i=1;i<=n;i++){//此题从第二行第二列考虑,及i=1,j=1开始考虑,边界值为0
for(int j=1;j<=m;j++){
if(i==1&&j==1){
dp[i][j]=1; //确定起始点为1
}
else if(i%2==0&&j%2==0){//偶数列行设为0即不能走
dp[i][j]=0;
}
else{
dp[i][j]=dp[i-1][j]+dp[i][j-1];//状态转换方程
}
}
}
printf("%d",dp[n][m]);
}
三、背包问题
(一)题目:
容量为10的背包,有5种物品,每种物品只有一个,其重量分别为5,4,3,2,1,其价值分别为1,2,3,4,5。
设计算法,实现背包内物品价值最大。
(二)解题思路:运用动态规划进行比较,将背包腾出当前重量的空间,加上价值,然后与原来不腾出空间的比较。
(三)解题代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
int w[6]={0,5,4,3,2,1};//物品重量
int v[6]={0,1,2,3,4,5};//物品价值
int dp[11]={0};//背包
for(int i=1;i<=5;i++){
for(int j=10;j>=w[i];j--){
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);//比较放入前和放入后谁大
}
}
printf("%d",dp[10]);
}
下面是两次for循环产生的结果:
四、爬楼梯问题
(一)题目:一个人上一个6个台阶的楼梯,每一走1个台阶或者2个,问有多少种走法?
(二)解题思路:这次的台阶数取决于上一个台阶和上两个的台阶,将上一个的台阶走法总数加上上两次台阶走法总数的的和就是这次的总数。
(三)解题代码:
#include<bits/stdc++.h>
using namespace std;
int f(int x){
int n=x;
if(n==1){
return 1;
}
if(n==2){
return 2;
}
else if(n>2){
int k=f(n-1)+f(n-2);//运用递归,这次结果取决于上一个或者上两个的次数
return k;
}
}
int main(){
int i=f(6);
printf("%d",i);
}
(四)也可以用数组解题(会省不少时间),代码如下:
`
``#include<bits/stdc++.h>
using namespace std;
int main(){
int dp[10]={0};
for(int i=1;i<=6;i++){
if(i==1){
dp[i]=1;
continue;
}
if(i==2){
dp[i]=2;
continue;
}
else{
dp[i]=dp[i-1]+dp[i-2];
}
}
printf("%d",dp[6]);
}
**
五、最长公共子串
**
(一)题目:有如下两串字符串 a=“abcdeadk”,b=“bbcdekd”,求出他们的最长公共子串是多长?
(二)运用动态规划比较,状态方程dp[i][j]=dp[i-1][j-1]+1;
(三)解题代码:
#include<bits/stdc++.h>
using namespace std;
int f(char* a,char* b){
int dp[100][100]={0};
int n=strlen(a);
int m=strlen(b);
int max=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i-1]==b[j-1]){
dp[i][j]=dp[i-1][j-1]+1;
}
if(max<dp[i][j]){
max=dp[i][j];
}
}
}
return max;
}
int main(){
int k=f("abcdeadk","bbcdekd");
printf("%d",k);
}