C++ 数字三角形(动态规划)

一、题目

    C++ 数字三角形(动态规划)_第1张图片

   有三种方法实现: 这里就求权值之和最大的,最小的类似就不分析了。

   1.自底向上  缺点:算法复杂,重复计算

   2.自顶向下  缺点:重复计算,浪费时间

   3.动态规划 思路和自底向上、自顶向下一样,只不过开多一个数组opt存储计算过的到达a[i][j]的权值之和最大的数

 

二、思路:

     方法1:自底向上

     1.从最后一行出发,例如,最后一行的2:

        如何得到走到2的权值之和最大数?(用递归

               那就要先得到走到左上角的数(7)的权值之和最大的数,正上方的数(4)的权值之和最大的数。

        再比较两者哪个大,大的一个加上最后一行的数2,就是走到2的权值之和最大的数;

        同样,如何得到走到7的权值之和最大的数?或者是走到4的权值之和的最大数

              就要先得到7的左上角的数(8)的权值之和的最大数,和正上方的数(1)的权值之和的最大数,再

        比较这两个权值之和哪个大,大的加上7;

        ……

        一直递推到第一行的数a[0][0],返回a[0][0]的值,然后回归,求得到达2的权值之和的最大数;

     2.比较走到最后一行(4,5,2,6,5)的各个权值之和,取得权值之和最大的数,然后输出

    实现程序:

//  数字三角形
//  7
//  3 8
//  8 1 0
//  2 7 4 4
//  4 5 2 6 5
//  找出从第一层到最后一层的一条路,使得经过的权值之和最小或者最大,
//  每一步只能向下或向右下方走。
//  第一种方法:自底向上的思路
//       缺点:算法有点复杂
//  思路:从下面一个数开始往上计算,取sum(i-1, j-1)和sum(i-1, j)的最大值(即前面的权值之和最大值),加上当前值

#include 

const int max = 100;
int a[max][max];
int sum(int i, int j);

int main(int argc, const char * argv[]) {
    int m = 0;
    int i, j, t, n;
    
    std::cin >> n; // 行数
    for(i = 0; i < n; i++) // 输入数字三角形
        for(j = 0; j <= i; j++)
            std::cin >> a[i][j];
    for(j = n-1; j >= 0; j--) {
        t = sum(n-1, j); // 计算走到最后一行每个数的权值之和
        if(m < t)
            m = t;
    }
    std::cout << m << std::endl;
    return 0;
}

// 递归:求走到第i行和第j列的权值之和最大的数
int sum(int i, int j) {
    int x, y;
    
    if(i < j)
        return 0;
    if(i == 0)
        return a[0][0];
    else {
        x = sum(i-1, j-1); // 求到达左上角的数的权值之和最大数
        y = sum(i-1, j); // 求到达正上方的数的权值之和最大数
        if(x > y)
            return x+a[i][j];
        else
            return y+a[i][j];
    }
}

测试结果:

C++ 数字三角形(动态规划)_第2张图片

方法2:自顶向下 和自底向上的思路刚好相反

    1.从a[0][0]出发:

       递推:得到从最后一行到正下方a[1][0]的权值之和最大的数,和从最后一行到右下方a[1][1]的权值之和最大的数

       回归:究竟是最后一行到a[1][0]的权值之和大,还是最后一行到达a[1][1]的权值之和大

                 取权值之和较大的,然后加上a[0][0],就是所求的权值之和最大的数

       那如何求得最后一行到a[1][0]的权值之和最大的数?或者是到a[1][1]的

       同样,求最后一行到正下方的数a[2][0]的权值之和,然后求最后一行到右下方的数a[2][1]的权值之和;

       取较大的权值之和,加上a[1][0]

       …

       一直递推,直到最后一行,然后返回最后一行的数

      接着回归求得从最后一行到a[0][0]的权值之和最大的数

    

    2.实现程序:

//  数字三角形
//  7
//  3 8
//  8 1 0
//  2 7 4 4
//  4 5 2 6 5
//  找出从第一层到最后一层的一条路,使得经过的权值之和最小或者最大,
//  每一步只能向下或向右下方走。
//  第二种方法:自顶向下的思路
//  缺点:重复计算,浪费时间

#include 

const int max = 100;
int a[max][max];
int sum(int i, int j, int n); // 取得到达最后一行的权值之和最大的数

int main(int argc, const char * argv[]) {
    int i, j, n;
    
    std::cin >> n; // 输入行数
    // 输入数字三角形
    for(i = 0; i < n; i++)
        for(j = 0; j <= i; j++)
            std::cin >> a[i][j];
    std::cout << sum(0, 0, n) << std::endl; // 调用函数
    return 0;
}

// 取得到达最后一行的权值之和最大的数
int sum(int i, int j, int n) {
    int x, y;
    
    if(i == n-1) // 到最后一行,返回最后一行的数
        return a[i][j];
    else {
        x = sum(i+1, j, n); // 求得最后一行到a[i+1][j]的权值之和最大的数
        y = sum(i+1, j+1, n); // 求得最后一行到a[i+1][j+1]的权值之和最大的数
        if(x>y) // 取权值之和较大的加上a[i][j]
            return x+a[i][j];
        else
            return y+a[i][j];
    }
}

方法3动态规划

1.思路:和自底向上和自顶向下思路一样,只不过,用多一个数组opt存储计算过的到达a[i][j]的权值之和最大的数

2.实现方法1:自顶向下(结合动态规划)

//  数字三角形
//  7
//  3 8
//  8 1 0
//  2 7 4 4
//  4 5 2 6 5
//  找出从第一层到最后一层的一条路,使得经过的权值之和最小或者最大,
//  每一步只能向下或向右下方走。
//  第三种方法:记忆化搜索,递归,自顶向下

#include 

const int MAX = 100;
int a[MAX][MAX];
int opt[MAX][MAX];
int sum(int i, int j, int n); // 计算

int main(int argc, const char * argv[]) {
    int i, j, n;
    
    std::cin >> n;
    memset(opt, 0, sizeof(opt)); // 清零
    // 输入数字三角形
    for(i = 0; i < n; i++)
        for(j = 0; j <= i; j++)
            std::cin >> a[i][j];
    std::cout << sum(0, 0, n) << std::endl; // 调用函数,求得最大的权值之和
    return 0;
}

// 递归:求最后一行到a[i][j]的权值之和最大的数
int sum(int i, int j, int n) {
    if(opt[i][j] != 0) // 之前已经求过,直接返回
        return opt[i][j];
    if(i == n-1) { // 到达最后一行
        opt[i][j] = a[i][j];
        return a[i][j];
    } else {
        opt[i+1][j] = sum(i+1, j, n); // 求得最后一行到a[i+1][j]的权值之和最大的数
        opt[i+1][j+1] = sum(i+1, j+1, n); // 求得最后一行到a[i+1][j+1]的权值之和最大的数
        if(opt[i+1][j] > opt[i+1][j+1]) // 取权值之和较大的加上a[i][j]
            return opt[i+1][j] + a[i][j]; // 最后一行到a[i][j]的权值之和最大的数
        else
            return opt[i+1][j+1] + a[i][j];
    }
}

方法2:自底向上(结合动态规划)

#include 

const int max = 100;
int a[max][max];
int opt[max][max];
void MaxSum(int n); // 求得数字三角形中从第一行到各个位置的权值之和最大的数

int main(int argc, const char * argv[]) {
    int i, j, n, t;
    
    std::cin >> n; // 输入行数
    memset(opt, 0, sizeof(opt)); // 清零
    // 输入数字三角形
    for(i = 0; i < n; i++)
        for(j = 0; j <= i; j++)
            std::cin >> a[i][j];
    // 调用函数:求得数字三角形中从第一行到各个位置的权值之和最大的数
    MaxSum(n);
    t = 0;
    // 比较最后一行的权值之和,找到最大的一个输出
    for(i = 0; i < n; i++) {
        if(t < opt[n-1][i])
            t = opt[n-1][i];
    }
    std::cout << t << std::endl;
    return 0;
}

// 求得数字三角形中从第一行到各个位置的权值之和最大的数
void MaxSum(int n) {
    int i, j;
    
    opt[0][0] = a[0][0];
    for(i = 1; i < n; i++) {
        // 计算每行第一个数的权值之和
        opt[i][0] = opt[i-1][0] + a[i][0];
        // 计算除开头和结尾之外的其他位置权值之和
        for(j = 1; j < i - 1; j++) {
            if(a[i][j]+opt[i-1][j] >= a[i][j]+opt[i-1][j-1])
                opt[i][j] = a[i][j]+opt[i-1][j];
            else
                opt[i][j] = a[i][j]+opt[i-1][j-1];
        }
        // 计算每行最后一个数的权值之和
        opt[i][i] = a[i][j]+opt[i-1][i-1];
    }
}

 

你可能感兴趣的:(数据结构)