动态规划算法学习

动态规划算法学习

数字三角形问题:

	         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*242次,同理第三层到第四层就需要4∗2∗24*2*2422,以此类推到第一层4∗2∗2∗24*2*2*24222,这就是解决这个问题需要进行的计算次数。

解决程序

#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
*/

总结

很多问题都可以用递归的方法解决,其思想就是将很大的问题进行分解成很多的小问题,然后解决掉小问题,拼在一起就是整个问题的解决办法。
当然这仅仅是一个很小的问题,复杂度较低。回头我再研究一个代数方程的例子。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值