动态规划基本问题
来源:互联网 发布:linux 宕机日志 编辑:程序博客网 时间:2024/06/11 19:59
一 数塔问题
dp[i][j]表示从第i行第j个数字到出发到达最底层的所有路径中能得到的最大和。
边界:dp[n][j]=f[n][j];
状态转移方程l dp[i][j]=max(dp[i+1][j],dp[i+1][j+1])+f[i][j];
动态规划的代码如下;
#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>using namespace std;const int maxn=110;int f[maxn][maxn];///存储数塔,第i行第j个数的值int dp[maxn][maxn];///从dii行第j个数字出发到达最底层的所有路径中的最大和int main(){ int n; while(scanf("%d",&n)!=EOF) { for(int i=1;i<=n;i++) for(int j=1;j<=i;j++) scanf("%d",&f[i][j]); for(int j=1;j<=n;j++)///边界 dp[n][j]=f[n][j]; for(int i=n-1;i>=1;i--) for(int j=1;j<=i;j++) { dp[i][j]=max(dp[i+1][j],dp[i+1][j+1])+f[i][j]; } printf("%d\n",dp[1][1]); } return 0;}
二 最大连续子序列的和
问题描述如下:
给定一个数字序列A1,A2,.......An, 求 i,j (1<=i<=j), 使得Ai +.......+Aj 最大,输出这个最大和。
dp[i]表示以 A[i] 为末尾的连续序列的最大和 (这里是说A[i]必须作为连续序列的末尾)。
边界:dp[0]=A[0];
下面想办法求解dp数组:
dp[i]必须是以 A[i] 为结尾的连续序列,那么只有两种情况:
(1) 这个最大和的连续序列只有一个元素,即以 A[i] 开始,以 A[i] 结尾。
(2)这个最大和的连续序列有多个元素,即从前面某处 A[p]开始 (p<i) ,一直到 A[i] 结尾
对第一种情况:最大和就是 A[i] 本身
对第二种情况,最大和是 dp[i-1] + A[i] , 即 A[p] +.........+A[i-1] +A[i] =dp[i-1] + A[i];
于是得到状态转移方程: dp[i] = max{ A[i] , dp[i-1]+ A[i] }.
代码如下:
///dp[i]为以A[i]末尾的连续序列的最大和///状态转移方程:dp[i]=max{A[i], dp[i-1]+A[i]}#include<cstdio>#include<iostream>#include<algorithm>using namespace std;const int maxn=1e4+10;int A[maxn],dp[maxn];int main(){ int n; scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d",&A[i]); dp[0]=A[0];///边界 for(int i=1;i<n;i++)///状态转移方程 dp[i]=max(A[i],dp[i-1]+A[i]); int k=0; for(int i=1;i<n;i++) if(dp[i]>dp[k]) k=i;///k记录dp[]数组最大值的下标 printf("%d\n",dp[k]); return 0; }
三 最长不下降子序列 (LIS)
问题描述如下:
在一个数字序列中,找到一个最长的子序列 (可以不连续) ,使得这个子序列是不下降的(非递减的)。
dp[i]表示以 A[i] 为结尾的最长不下降子序列的长度 (和最大连续子序列一样,以A[i] 为结尾是强制要求)。那么对于
A[i] 来说就会有两种可能:
(1) 如果存在A[i] 之前的元素 A[j] ,是的 A[j] <= A[i] 且 dp[j] +1 >dp[i] ( 即把 A[i] 跟在以A[j] 结尾的LIS后面时能比当
前以A[i] 结尾的LIS 长度更长)。
(2) 如果A[i]之前的元素都比 A[i] 大,那么A[i] 就只好自己形成一个LIS 但是长度为1.
边界 dp[i] =1;
状态转移方程: dp[i] = max{1,dp[ j ]+1 } (j=1,2,3,.....i-1&& A[j]<=A[i])
代码如下:
///dp[i]表示以A[i]为末尾的最长不下降子序列的长度///状态转移方程dp[i]=max{1,dp[j]+1} j<i&&A[j]<A[i]///边界 dp[1]=1;#include<cstdio>#include<iostream>#include<algorithm>using namespace std;const int N=100;int A[N],dp[N];int main(){ int n; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&A[i]); int ans=-1;///记录最大的dp[i] for(int i=1;i<=n;i++) { dp[i]=1; for(int j=1;j<i;j++) { if(A[i]>=A[j]&&(dp[j]+1>dp[i])) dp[i]=dp[j]+1; } ans=max(ans,dp[i]); } printf("%d\n",ans); return 0;}
四 最长公共子序列 (LCS)
问题描述:
给定两个字符串 (或数字序列) A 和 B,求一个字符串,使得这个字符串是A 和 B 的最长公共部分 (子序列可以不连续)。
样例:字符串 "sadstory"与 "adminsorry"的最长公共子序列为 "adsory",长度为6.
dp[i][ j]表示字符串 A 与字符串 B 的j号位之前的的 LCS 长度,那么可以根据 A[i] 和 B[j]的情况,分为两种决策:
(1)若A[i]==B[j], 则字符串 A 与字符串 B 的 LCS 增加了一位, 即有 dp[i][j]=dp[i-1][j-1]+1,例如 样例中的dp[4][6]表
示"sads"与"admins"的LCS长度,比较A[4]与A[6],发现两者都是"s",因此dp[4][6]就等于dp[3][5]+1,即为3
(2)若 A[i]!=B[j] ,则字符串A 的i号位和字符串B的j号位之前的 LCS 无法延长,因此dp[i][j] 将会继承 dp[i-1][j] 与
dp[i][j-1] 中的较大值,即有dp[i][j]=max{dp[i-1][j], dp[i][j-1]}.
边界:dp[i][0] = dp[0][j] =0; (0<=i<=n, 0<=j<=m) 由边界条件递推至整个dp数组
状态转移方程:
dp[i][j] = dp[i-1][j-1]+1 , A[i]==B[j]
max{dp[i-1][j],dp[i][j-1]} , A[i]!=B[j]
代码;
#include<cstdio.>#include<iostream>#include<cstring>#include<algorithm>using namespace std;const int N=100;char A[N],B[N];int dp[N][N];int main(){ int n; gets(A+1); gets(B+1); int lenA=strlen(A+1); int lenB=strlen(B+1); ///边界 for(int i=0;i<=lenA;i++) dp[i][0]=0; for(int j=0;j<=lenB;j++) dp[0][j]=0; ///状态转移方程 for(int i=1;i<=lenA;i++) { for(int j=1;j<=lenB;j++) if(A[i]==B[j]) { dp[i][j]=dp[i-1][j-1]+1; } else { dp[i][j]=max(dp[i-1][j],dp[i][j-1]); } } printf("%d\n",dp[lenA][lenB]); return 0;}
五 最长回文子串
问题描述:
给出一个字符串 S,求 S 的最长回文子串
样例: 字符串 "PATZJUJZTACCBCC " 的最长回文子串为 "ATZJUJZTA" ,长度为9
dp[i][j] 表示S[i] 至 S[j] 所表示的子串是否是回文子串,是则为 1,不是为 0,这样根据 S[i] 是否等于 S[j] ,可以把转移
情况分为两类:
(1) 若 S[i]==S[j],那么只要S[i+1] 至 S[j-1]是回文子串,S[i] 至 S[j] 就是回文子串;如果S[i+1] 至 S[j-1] 不是回文子串
,则S[i]至S[j] 也不是回文子串。
(2)若S[i]!=S[j], 那么S[i]至S[j] 一定不是回文子串。
由此可以写出状态转移方程:
dp[i][j]= dp[i+1][j-1] S[i] == S[j]
0 S[i] != S[j]
边界: dp[i][i]=1; dp[i][i+1]=(S[i]==S[i=1])?1:0
枚举方式:根据递推写法从边界出发的原理,注意到边界表示的是长度为1 和 2 的字符串,且每次转移时都对子串的
长度减一,因此不妨考虑按子串的长度和子串的初始位置进行枚举,即第一遍将长度为 3 的子串的dp值全部求出,第
二遍通过第一遍的结果将长度为4 的子串的dp值。。。。。。这样就能避免状态无法转移的问题。
代码:
#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>using namespace std;const int maxn=1e3+10;char s[maxn];int dp[maxn][maxn];int main(){ gets(s); int len=strlen(s); int ans=1; memset(dp,0,sizeof(dp)); ///边界 for(int i=0;i<len;i++) { dp[i][i]=1; if(i<len-1) { if(s[i]==s[i+1]) dp[i][i+1]=1; ans=2; } } ///状态转移方程 for(int L=3;L<=len;L++) { for(int i=0;i+L-1<len;i++)///枚举子串的起始端点 { int j=i+L-1;///子串的右端点 if(s[i]==s[j]&&dp[i+1][j-1]==1) { dp[i][j]=1; ans=L;///更新最长回文子串的长度 } } } printf("%d\n",ans); return 0;}
- 动态规划基本问题
- 动态规划基本问题
- 动态规划基本问题
- 两个基本的动态规划问题
- 【动态规划】三种基本背包问题
- 动态规划基本思想
- 动态规划 基本解法
- 基本动态规划讲解
- 动态规划-基本思想
- 动态规划求解01背包相关的基本问题
- 背包问题与动态规划的基本思想
- 动态规划求解01背包相关的基本问题
- 【学习动态规划】两个基本的小问题
- 动态规划的基本思想
- 动态规划的基本思想
- 动态规划算法 - 基本介绍
- 动态规划----贪心的动态规划问题
- 动态规划问题
- Python基础(三)
- Sublime Text的配置
- WP Super Cache 安装与设置详解
- 常见排序算法及其C语言实现(二)
- RecyclerView 之通用适配
- 动态规划基本问题
- 编写Java程序,显示五个加减法测验的正确率和用时
- 动态规划(三.LCS)
- intent的使用
- 简单记录spring在控制台向页面传參的几种方法
- Linux 常用查看日志命令
- 图像语义分割(1)- FCN
- centos7搭建hadoop集群
- Map四种遍历方式以及增删改查方法总结