我分析做动规的题目有两种方式:
1、记忆化递归性(递归+备忘录)
2、递推型
其中,记忆化递归性用递归的方式做,并且要设置备忘录数组,以免重复调用计算,浪费时间;递推型动规要确定出状态,并列出状态转移方程,这是比较难的。
下面三个小题都是有关动态规划的题目:
目录
一、数字三角形
Description
下图给出了一个数字三角形,请编写一个程序,计算从顶至底的某处的一条路径,使该路径所经过的数字和最大
Input
有很多个测试案例,对于每一个测试案例, 通过键盘逐行输入,第1行是输入整数(如果该整数是0,就表示结束,不需要再处理),表示三角形行数n,然后是n行数
Output
输出最大值
Example Input
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
Example Output
30
代码块
(1) 记忆递归性型动规程序
///数字三角形记忆递归性型动规程序
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 101
int D[maxn][maxn];
int n;
int maxsum[maxn][maxn];//备忘录
int Maxsum(int i,int j)//Maxsum函数表示D[i][j]点到底边的最大值
{
if(maxsum[i][j]!=-1)
return maxsum[i][j];
if(i==n)
maxsum[i][j]=D[i][j];
else
{
int x=Maxsum(i+1,j);
int y=Maxsum(i+1,j+1);
maxsum[i][j]=max(x,y)+D[i][j];
}
return maxsum[i][j];
}
int main()
{
int i,j;
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
{
cin>>D[i][j];
maxsum[i][j]=-1;//设置备忘录
}
cout<<Maxsum(1,1)<<endl;
return 0;
}
(2)递推型
///数字三角形递推形式
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 101
int D[maxn][maxn];
int n;
int maxSum[maxn][maxn];
int main()
{
int i,j;
cin >> n;
for(i=1;i<=n;i++)
for(j=1;j<=i;j++)
cin >> D[i][j];
for( int i = 1;i <= n;i++)
maxSum[n][i] = D[n][i];
for( int i = n-1; i>= 1;i--)
for( int j = 1; j <= i; ++j )
{
maxSum[i][j] = max(maxSum[i+1][j],maxSum[i+1][j+1])+D[i][j];
}
cout << maxSum[1][1] << endl;
}
(3)递推型的空间优化
直接用D的 第n行替代maxSum即可。 节省空间,时间复杂度不变
///数字三角形空间优化
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 101
int D[maxn][maxn];
int n;
int *maxSum;
int main()
{
int i,j;
cin >> n;
for(i=1;i<=n;i++)
for(j=1;j<=i;j++)
cin >> D[i][j];
maxSum=D[n];//指针指向D数组第n行的首地址
for( int i = n-1; i>= 1;i--)
for( int j = 1; j <= i; ++j )
{
maxSum[j] = max(maxSum[j],maxSum[j+1])+D[i][j];
}
cout << maxSum[1] << endl;
}
二、最长上升子序列(百练2757)
描述
一个数的序列bi,当b1 < b2 < ... < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, ..., aN),我们可以得到一些上升的子序列(ai1, ai2, ..., aiK),这里1 <= i1 < i2 < ... < iK <= N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8).
你的任务,就是对于给定的序列,求出最长上升子序列的长度。
输入
输入的第一行是序列的长度N (1 <= N <= 1000)。第二行给出序列中的N个整数,这些整数的取值范围都在0到10000。
输出
最长上升子序列的长度。
样例输入
7
1 7 3 5 9 4 8
样例输出
4
分析:
1.找子问题
“求以ak(k=1, 2, 3…N)为终点的最长上升子序列的 长度” 一个上升子序列中最右边的那个数,称为该子序列的 “终点”。 虽然这个子问题和原问题形式上并不完全一样,但 是只要这N个子问题都解决了,那么这N个子问题的解中, 最大的那个就是整个问题的解。
2、确定状态
子问题只和一个变量-- 数字的位置相关。因此序列中数的 位置k 就是“状态”,而状态 k 对应的“值”,就是以ak做为 “终点”的最长上升子序列的长度。 状态一共有N个。
3. 找出状态转移方程:
maxLen (k)表示以ak做为“终点”的 最长上升子序列的长度那么:
初始状态:maxLen (1) = 1
maxLen (k) = max { maxLen (i):1<=i < k 且 ai < ak且 k≠1 } + 1 若找不到这样的i,则maxLen(k) = 1
maxLen(k)的值,就是在ak左边,“终点”数值小于ak ,且长度 最大的那个上升子序列的长度再加1。因为ak左边任何“终点”小 于ak的子序列,加上ak后就能形成一个更长的上升子序列。
///最长上升子序列(“人人为我”递推型动归程序)
///时间复杂度O(N2)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<math.h>
#include<string>
#include<algorithm>
using namespace std;
const int maxn=1010;
int a[maxn];
int maxlen[maxn];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
maxlen[i]=1;
}
for(int i=2;i<=n;i++)//每次求以第i个数为终点的最长上升子序列的长度
for(int j=1;j<i;j++)
{
if(a[i]>a[j])//察看以第j个数为终点的最长上升子序列
maxlen[i]=max(maxlen[i],maxlen[j]+1);
}
cout<<*max_element(maxlen+1,maxlen+n+1)<<endl;//STL函数,在maxlen中找到最大值
return 0;
}
三、最长公共子序列(poj1458)
题意:
给出两个字符串,求出这样的一 个最长的公共子序列的长度:子序列 中的每个字符都能在两个原串中找到, 而且每个字符的先后顺序和原串中的 先后顺序一致。
Sample Input
abcfbc abfcab
programming
contest abcd mnp
Sample Output
4 2 0
分析:
输入两个串s1,s2, 设MaxLen(i,j)表示: s1的左边i个字符形成的子串,与s2左边的j个 字符形成的子串的最长公共子序列的长度(i,j从0 开始算) MaxLen(i,j) 就是本题的“状态”。
#include <iostream>
#include <cstring>
using namespace std;
char sz1[1000];
char sz2[1000];
int maxLen[1000][1000];
int main()
{
while( cin >> sz1 >> sz2 )
{
int length1 = strlen( sz1);
int length2 = strlen( sz2);
int nTmp;
int i,j;
for( i = 0;i <= length1; i ++ )
maxLen[i][0] = 0;
for( j = 0;j <= length2; j ++ )
maxLen[0][j] = 0;
for( i = 1;i <= length1;i ++ )
{
for( j = 1; j <= length2; j ++ )
{
if( sz1[i-1] == sz2[j-1] )
maxLen[i][j] = maxLen[i-1][j-1] + 1;
else
maxLen[i][j] = max(maxLen[i][j-1], maxLen[i-1][j]);
}
}
cout << maxLen[length1][length2] << endl;
}
return 0;
}
北大郭炜老师的算法视频讲解:https://www.icourse163.org/learn/PKU-1001894005?tid=1002783037#/learn/announce