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++、機器學習等。

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