DP专题 1 | 数字三角形 - POJ 1163

从本篇开始,准备做一系列的专题讲解,主要参考《算法竞赛入门经典》、《算法竞赛进阶指南》两本书。主要是为了能够更加系统的讲解各个知识点,这两本书已经讲得很好了,建议准备ACM学习以及想深入学习算法的同学购买。

每一个专题都会持续比较久的时间,就比如拿动态规划DP来说,种类非常多,从浅入深可以说是一次深潜,刚开始可能还好,后面会比较困难~

 

另外本号的更新频率一般是一周2-3篇,关注本号的童鞋请耐心一点哈。

 

来看题:

The Triangle

Description

7
3   8
8   1   0
2   7   4   4
4   5   2   6   5

(Figure 1)

Figure 1 shows a number triangle. Write a program that calculates the highest sum of numbers passed on a route that starts at the top and ends somewhere on the base. Each step can go either diagonally down to the left or diagonally down to the right. 

Input

Your program is to read from standard input. The first line contains one integer N: the number of rows in the triangle. The following N lines describe the data of the triangle. The number of rows in the triangle is > 1 but <= 100. The numbers in the triangle, all integers, are between 0 and 99.

Output

Your program is to write to standard output. The highest sum is written as an integer.

Sample Input

5
7
3 8
8 1 0 
2 7 4 4
4 5 2 6 5

Sample Output

30


这是DP的入门经典题目,思路的关键是针对位置[i][j]进行状态转移,dp[i][j]表示从第一行走到第i行第j列的最大和。

 

动态规划里面有很多问题的dp都是以位置i开始或者结束,比如下一篇要讲得LIS问题,不过本题是一个二维的表示。

 

本题用递推的方式就已经很好实现了,建议不熟的同学再尝试递归的方法。

 

状态转移方程:

dp[i][j] = A[i][j] + max(dp[i-1][j], dp[i-1][j-1])

 

A[i][j]是当前位置的值,加上上一行相邻的两个值的较大值,保证了最优子结构。

 

注意j-1小于0的特殊处理。

 

时间复杂度:dp的状态总数*决策复杂度,这里是二维的,状态总数也就是i*j,决策只有2中决策。总复杂度O(n2)。

 

进阶的书里面和本题解法一致,算法竞赛入门里面是从d[i+1][j+1]到d[i][j],思路恰好是逆序的,都可以看看。

 

最重要的是找到转移方程和边界。

 

源代码:G++

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <set>
#include <vector>
#include <string>
#include <string.h>
using namespace std;

#ifdef __MSYS__
#define printfd printf
#else
#define printfd
#endif

int main() {
#ifdef __MSYS__
    freopen("test.txt", "r", stdin);
#endif
    int dp[100][100] = {0};
    int a[100][100] = {0};

    int n;
    scanf("%d", &n);

    for(int i=0; i<n; ++i){
        for(int j=0; j<i+1; ++j){
            scanf("%d", &a[i][j]);
        }
    }

    dp[0][0] = a[0][0];
    for(int i=1; i<n; ++i){
        for(int j=0; j<i+1; ++j){
            if(j-1>=0){
                dp[i][j] = a[i][j] + std::max(dp[i-1][j], dp[i-1][j-1]);
            }else{
                dp[i][j] = a[i][j] + dp[i-1][j];
            }
            printfd("%d %d %d\n", i, j, dp[i][j]);
        }
    }

    int m=0;
    for(int j=0; j<n; ++j){
        m = std::max(dp[n-1][j], m);
    }
    printf("%d\n", m);

    return 0;
}

PS~这里我用宏控制了printfd,只有在我的msys环境下才会生效,这样就可以不用改代码直接提交poj评测系统了。

 

个人公众号:ACM算法日常

专注于基础算法的研究工作,深入解析ACM算法题,五分钟阅读,轻松理解每一行源代码。内容涉及算法、C/C++、机器学习等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章