算法入门 - 动态规划

Posted So istes immer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法入门 - 动态规划相关的知识,希望对你有一定的参考价值。

例题1 数字三角形

        7
       3 8
     8  1  0
   2  7  4   4
4   5  2   6   5
在上面的数字三角形中寻找一条从顶部到底边的路径。使得路径上所经过的数字之和最大。路径上的每一步都只能往左下或右下走。只需要求出这个最大和即可,不必给出具体路径。
1<三角形的行数<=100,数字为0-99
输入
5        //三角形的行数
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
要求输出最大和

思路 递归

D(r,j):第r行第j个数字
MaxSum(r,j):从D(r,j)到底边的各条路径中,最佳路径的数字之和
我们现在要求MaxSum(1,1) 
对于N行的三角形
if(r==N)
    MaxSum(r,j) = D(r,j)
else
    MaxSum(r,j) = Max { MaxSum(r+1,j),MaxSum(r+1,j+1) }

#include <iostream>
#include <algorithm>
#define MAX 101

using namespace std;

int D[MAX][MAX];
int n;

int MaxSum(int i, int j) {
	if(i==n)
		return D[i][j];
	int x = MaxSum(i+1, j);
	int y = MaxSum(i+1, j+1);
	return max(x,y) + D[i][j]; 
}

int main() {
	int i,j;
	cin >> n;
	for(i=1;i<=n;i++)
		for(j=1;j<=i;j++)
			cin >> D[i][j];
	cout << MaxSum(1, 1) << endl;
	
	return 0;
}

改进 避免重复计算

没算出一个MaxSum(i,j)就保存起来,下次可以直接用

#include <iostream>
#include <algorithm>
#define MAX 101

using namespace std;

int D[MAX][MAX];
int n;
int maxSum[MAX][MAX];

int MaxSum(int i, int j) {
	if(maxSum[i][j] != -1)
		return maxSum[i][j];
	if(i==n)
		return D[i][j];
	int x = MaxSum(i+1, j);
	int y = MaxSum(i+1, j+1);
	return max(x,y) + D[i][j]; 
}

int main() {
	int i,j;
	cin >> n;
	for(i=1;i<=n;i++)
		for(j=1;j<=i;j++) {
			cin >> D[i][j];
			maxSum[i][j] = -1;
		}		
	cout << MaxSum(1, 1) << endl;
	
	return 0;
}

将递归转成递推

#include <iostream>
#include <algorithm>
#define MAX 101

using namespace std;

int D[MAX][MAX];
int n;
int maxSum[MAX][MAX];

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;	
	return 0;
}

空间优化

直接用二维数组D的第n行替代maxSum即可

#include <iostream>
#include <algorithm>
#define MAX 101

using namespace std;

int D[MAX][MAX];
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];	//maxSum指向第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;	
	return 0;
}

例题2 最长上升子序列

假设我们有一个序列 b i,当b1 < b2 < … < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, …, aN),我们也可以从中得到一些上升的子序列(ai1, ai2, …, aiK),这里1 <= i1 < i2 < … < iK <= N,但必须按照从前到后的顺序。比如,对于序列(1, 7, 3, 5, 9, 4, 8),我们就会得到一些上升的子序列,如(1, 7, 9), (3, 4, 8), (1, 3, 5, 8)等等,而这些子序列中最长的(如子序列(1, 3, 5, 8) ),它的长度为4,因此该序列的最长上升子序列长度为4。
输入:第一行是序列的长度(1<=N<=1000),第二行给出序列中的N个整数
输出:最长上升子序列的长度

解题思路

①找子问题:求以ak(k=1,2,3...N)为终点的最长上升子序列的长度
②子问题只和一个变量---数字的位置有关,因此序列中ak的位置就是“状态”,一共有N个状态

③找出状态转移方程 
maxLen(k)表示以ak为"终点"的最长上升子序列的长度
k=1, maxLen(1) = 1
k>1,maxLen(k) = max{ maxLen(i): 1 <= i < k 且 ai < ak} + 1,如果ai(i1<i<k)都大于ak,那么maxLen(k)=1

#include <iostream>
#include <algorithm>
#define MAXN 1010

using namespace std;

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) {
		for(int j = 1;j < i;++j) {
			if(a[i] > a[j])
				maxLen[i] = max(maxLen[i],maxLen[j]+1);
		}
	}
	cout << *max_element(maxLen+1,maxLen+N+1);
	return 0;
}

以上是关于算法入门 - 动态规划的主要内容,如果未能解决你的问题,请参考以下文章

动态规划算法入门练习 (python实现)

Java入门算法(动态规划篇1:初识动规)

动态规划快速入门

Java入门算法(动态规划篇1:初识动规)

Java入门算法(动态规划篇2:01背包精讲)

Java入门算法(动态规划篇2:01背包精讲)