动态规划算法学习
数字三角形问题:
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
在上面的数字三角形中寻找一条从顶部到底边的路径,使得路径_上所经过的数字之和最大。路径上的每一步都只能往左下或右下走。只需要求出这个最大和即可,不必给出具体路径。
递归解决法
解决思路是采用递归算法,每个节点只加上,左下和右下两个数据,即
int x = MaxSum(i+1, j); // 左下
int y = MaxSum(i+1, j+1); // 右下
这样每一层就会形成两条分支,直到递归到最后一层,求出最大的和,然后退出。
对程序进入MaxSum进行计数可以得到一共32次,即2^5
复杂度计算
252^525:是怎么计算得到的
应该反推这个问题,当第四层到第五层时,一共需要有4∗24*24∗2次,同理第三层到第四层就需要4∗2∗24*2*24∗2∗2,以此类推到第一层4∗2∗2∗24*2*2*24∗2∗2∗2,这就是解决这个问题需要进行的计算次数。
解决程序
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
#define MAX 101
int D[MAX][MAX];
int n;
int max_count = 1;
int MaxSum(int i, int j)
{
max_count++;
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()
{
cin >> n;
for(int i = 1; i <= n;i++)
{
for(int j = 1;j <= i;j++)
{
cin >> D[i][j];
}
}
cout << MaxSum(1, 1) << endl;
cout << "max_count:" << max_count << endl;
cout << max_count << endl;
return 0;
}
/*
输入:
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输出:
30
max_count:32
*/
可以看出这种递归的做法,程序的执行复杂度还是比较高的当层数递增的时候,2n2^n2n是一个超大数量级的任务。
从上面我们其实可以发现,中间有大量的重复计算,比如第四层到第五层的7->5这个计算,每次到7的时候都会进行一次计算,就会重复。所以,解决的办法是,把中间的计算结果存储下来就好。
改进递归解决法
把中间结果存储下来,我们也需要逆向思考,存储的过程中需要从尾部开始存储,这样可以保证计算得到的结果是最大的。
2 7 4 4
4 5 2 6 5
这里可以存储第四层的结果,给第三层使用。比如,2+4=6 < 2+5=7 此时在第四层2的位置存储7就好,第三层遍历到此处时,可以直接取出7,就避免了重复计算。
复杂度计算
这样的复杂度的计算就会变成加法,第四层到第五层的计算次数,2 * 4 =8 第三层到第四层的计算次数 2 * 3 = 6 ,同理全部的计算次数8 + 6 + 4 + 2=20,计算次数明显减少,当数据层数增加时,计算的复杂度也仅仅是一加法的形式增加,而不是以次幂的形式增加。
解决程序
#include <iostream>
#include <string>
#include <algorithm>
#include <fstream>
#include <sstream>
using namespace std;
#define MAX 101
int D[MAX][MAX];
int maxSum[MAX][MAX];
int n;
int max_count = 1;
int MaxSum(int i, int j)
{
max_count++;
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()
{
ifstream infile("test_data.txt"); //这里我改成了文档导入数据,不然每次都要手动输入
infile >> n;
for(int i = 1; i <= n;i++)
{
for(int j = 1;j <= i;j++)
{
infile >> D[i][j];
maxSum[i][j] = -1;
}
}
cout << MaxSum(1, 1) << endl;
cout << "max_count:" << max_count << endl;
return 0;
}
/*输出
30
max_count:22
*/
递归改进为递推
把递归改为递推,一般情况下都可以减少程序运行内存和执行时间。但是其思想就是刚刚讨论的,逆推整个过程,达到冬天解决的目的。我们定义一个maxSum存储每一层正确的结果,不需要的就可以抛弃掉。
复杂度计算
此时的复杂度计算,是每一层的个数4 + 3 + 2 + 1 = 10,少了递归的推进,我们可以不用递归到最后一层。
解决程序
#include <iostream>
#include <string>
#include <algorithm>
#include <fstream>
#include <sstream>
using namespace std;
#define MAX 101
int D[MAX][MAX];
int *maxSum;
int n;
int max_count = 1;
int main()
{
ifstream infile("test_data.txt");
infile >> n;
for(int i = 1; i <= n;i++)
{
for(int j = 1;j <= i;j++)
{
infile >> D[i][j];
}
}
maxSum = D[n];
for(int i = n-1; i >= 1;i--)
{
for(int j = 1;j <= i;j++)
{
max_count++;
maxSum[j] = max(maxSum[j], maxSum[j+1]) + D[i][j];
}
}
cout << "Sum: " <<maxSum[1]<< endl;
cout << "max_count: " << max_count << endl;
return 0;
}
/*输出
30
max_count:11
*/
总结
很多问题都可以用递归的方法解决,其思想就是将很大的问题进行分解成很多的小问题,然后解决掉小问题,拼在一起就是整个问题的解决办法。
当然这仅仅是一个很小的问题,复杂度较低。回头我再研究一个代数方程的例子。