三角形最大路径和

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 自顶向下的方式
pair<int, vector<int>> maximumTotal(vector<vector<int>>& triangle) {
    int n = triangle.size();
    if (n == 0) return {0, {}};

    /*    dp[i][j] 表示从第 i 行第 j 列到底部的最大路径和
        要明白dp这个变量的数据格式是什么样的,它是一个类似这样的格式:
            就是一个二维数组,n*n的二维数组,矩阵一样
    */
    vector<vector<int>> dp(n, vector<int>(n, 0));    

    // parent[i][j] 记录 (i, j) 位置的前驱节点 (i-1, k)
    vector<vector<int>> parent(n, vector<int>(n, -1));

    // 初始化第一行
    dp[0][0] = triangle[0][0];

    int currenti, currentj;
    // 自顶向下计算 dp 数组
    for (int i = 1; i < n; ++i) {
        for (int j = 0; j <= i; ++j) {
            // 处理左边界
            if (j == 0) {  
                /* 
                    dp[i][j]表示当前这个[i][j]节点对于的前面所有路径的最大和
                    ,由于左边界的前驱节点只能有一个,就是[i-1][j]这个节点,即[i-1][0]这个节点
                    所以[i][j]节点对于的前面所有路径的最大和dp[i][j]就是前驱节点的最大路径和再
                    加上当前这个节点的值triangle[i][j],右边界的节点同理
                
                */
                dp[i][j] = triangle[i][j] + dp[i - 1][j];  
            
                /*
                    在数字三角形问题中,每个位置 (i, j) 的前驱节点只能是 (i-1, j-1) 或 (i-1, j)。
                    因此,前驱节点的行索引一定是 i-1,不需要额外存储。所以下面的这行代码只存储了j
                */
                parent[i][j] = j;  // 前驱节点是 (i-1, j)   parent[1][0]=0
            }
            // 处理右边界
            else if (j == i) {
                dp[i][j] = triangle[i][j] + dp[i - 1][j - 1];
                currenti = i - 1;
                currentj = j - 1;
                parent[i][j] = j - 1;  // 前驱节点是 (i-1, j-1)
            }
            // 中间位置,由于[i][j]面临着两个前驱节点[i-1][j - 1]和[i-1][j],
            // 那么此时就需要判断这两个前驱节点的最大路径和哪个大,哪个大就选择用当前[i][j]的节点值
            // triangle[i][j]加上这个大的最大路径和
            else {
                if (dp[i - 1][j - 1] > dp[i - 1][j]) {
                    dp[i][j] = triangle[i][j] + dp[i - 1][j - 1];
                    currenti = i - 1;
                    currentj = j - 1;
                    parent[i][j] = j - 1;  // 前驱节点是 (i-1, j-1)
                } else {
                    dp[i][j] = triangle[i][j] + dp[i - 1][j];
                    currenti = i - 1;
                    currentj = j;
                    parent[i][j] = j;  // 前驱节点是 (i-1, j)
                }
            }
            printf("dp[%d][%d] = triangle[%d][%d] + dp[%d][%d] = %d\n", i, j, i, j, currenti, currentj, dp[i][j]);
        }
    }

    // 找到最大路径和的终点
    int max_sum = *max_element(dp[n - 1].begin(), dp[n - 1].end());
    // max_element(dp[n - 1].begin(), dp[n - 1].end())找到dp中最大的路径和的位置对应的迭代器,
    // 然后再减去起始位置的迭代器dp[n - 1].begin(),就得到了最大的路径和这个位置相对起点的位移量,也就是索引,
    // 这个索引就是最大的路径和当前位置在最后一行的索引位置了。
    int end_index = max_element(dp[n - 1].begin(), dp[n - 1].end()) - dp[n - 1].begin();
    // printf("end_index = %d\n",end_index);
    
    // 先把记录每个节点的前驱节点的二维数组parent打印出来
    // 遍历二维数组
//    cout<<"\n--------------开始打印前驱节点的二维数组------------"<<endl;
//    for (int i = 0; i < n; i++) {          // 遍历行
//        for (int j = 0; j < n; j++) {      // 遍历列
//            cout << "parent[" << i << "][" << j << "] = " << parent[i][j] << " ";
//        }
//        cout << endl; // 每行结束后换行
//    }
//    cout<<"-----------------------------------------------------\n"<<endl;
    // 回溯构建路径
    vector<int> path;
    int current_row = n - 1, current_col = end_index;
    while (current_row >= 0) {
        path.push_back(triangle[current_row][current_col]);
        if (current_row > 0) {
            current_col = parent[current_row][current_col];
        }
        current_row--;
    }
    reverse(path.begin(), path.end());  // 反转得到正序

    return {max_sum, path};
}

int main() {
    vector<vector<int>> triangle = {
          {7},
        {3, 8},
       {8, 1, 0},
      {2, 7, 4, 4},
     {4, 5, 2, 6, 5}
    };
           //  7 3 8 7 5   30
    auto result = maximumTotal(triangle);
    cout << "最大路径和为: " << result.first << endl;
    cout << "最大路径为: ";
    for (int num : result.second) {
        cout << num << " ";
    }
    return 0;
}

 

posted @ 2025-03-16 16:41  SIPnnnnn  阅读(9)  评论(0)    收藏  举报