【動態規划基礎】數字金字塔


題目鏈接:1258:【例9.2】數字金字塔

 

 

1258:【例9.2】數字金字塔


時間限制: 1000 ms         內存限制: 65536 KB
提交數: 9635     通過數: 5467

【題目描述】

觀察下面的數字金字塔。寫一個程序查找從最高點到底部任意處結束的路徑,使路徑經過數字的和最大。每一步可以從當前點走到左下方的點也可以到達右下方的點。

在上面的樣例中,從13到8到26到15到24的路徑產生了最大的和86。

【輸入】

第一個行包含R(1≤ R≤1000),表示行的數目。

后面每行為這個數字金字塔特定行包含的整數。

所有的被供應的整數是非負的且不大於100。

 

【輸出】

單獨的一行,包含那個可能得到的最大的和。

【輸入樣例】

5
13
11 8
12 7  26
6  14 15 8
12 7  13 24 11

【輸出樣例】

86

 

 

提交 統計信息 提交記錄

 

 

 

  

 【動態規划基礎】數字金字塔

  經典的動態規划

方法一:貪心

  題目里說了,從上往下走,要求出路徑的最大值,那么我最先想到的方法就是——

  你每次都走最大的一邊就可以了啊。

  就像這樣:

  

  但是,如果你是一個善於造數據來驗證你的算法的人,你很快就會發現自己算法的問題,就像這樣:

1
2 1
1 2 9
1 2 8 9
3 3 2 9 9

  在這一組數據中,如果使用貪心算法,就會一開始就左邊,從而錯失下面全是9的右邊。

  也就是說,貪心算法看得太近了,沒有顧全大局。

方法二:搜索 & 記憶化搜索

  像我這樣的暴力型選手,碰見什么題都要用搜索寫一遍。可以對拍不說,寫不出來的時候,還可以騙分。

 

#include <bits/stdc++.h>
using namespace std;

int jzt[1005][1005], maxn, summ, n;

void dfs(int i, int j){
    if(i==n){
        maxn=maxn>summ?maxn:summ;
    }
    else{
        summ+=jzt[i+1][j];
        dfs(i+1, j);
        summ-=jzt[i+1][j];
        summ+=jzt[i+1][j+1];
        dfs(i+1, j+1);
        summ-=jzt[i+1][j+1];
    }
}

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

    dfs(0, 0);
    printf("%d", maxn+jzt[0][0]);

    return 0;
}

 

 

   T了。。

  但是沒關系,我們還有搜索優化利器:記憶化。

  

 

 

   通過觀察與推理,我們會發現,中間的這些點到底端的最優解會被重復計算,所以我們要把這些狀態記憶下來,四舍五入約等於全部記下來(其實是我懶得做特判)。。

#include <bits/stdc++.h>
using namespace std;

int n, bz[1005][1005], jzt[1005][1005];

int dfs(int x, int y){
    if(x==n-1) return jzt[x][y];
    else{
        if(bz[x][y]) return jzt[x][y];
        else{
            bz[x][y]=1;
            jzt[x][y]+=max(dfs(x+1, y), dfs(x+1, y+1));
            return jzt[x][y];
        }
    }
}

int main(){
    scanf("%d", &n);
    for(int i=0; i<n; i++){
        for(int j=0; j<=i; j++){
            scanf("%d", &jzt[i][j]);
        }
    }
    printf("%d", dfs(0, 0));

    return 0;
}

 

  這次,就功地AC了,不得不感嘆記憶化的強大。其實記憶化搜索的用時就已經相當於動態規划了(這也是為什么我把搜索學好后才學動態規划)。。

方法三:動態規划(遞推)

  現在,想象一下,通過某些神奇的算法,你已經成功走到了倒數第二行的某一個位置,那么下一步應該怎么走呢?

  很明顯,只要走最大的那個就可以了。因為這里只有最后一步了,局部最優解就等於全局最優解。

  但事實是,你並不知道這個神奇的算法。。。所以你還是不知道你會走到倒數第二行的哪個位置。

  但是,無論如何,你最終都會走到倒數第二行的n-1個點之一。那么我們就把到任意一個點的全局最優解求出來吧。

  那么知道了倒數第二行怎么走,那么我們就再進一步吧。

通過某些神奇的算法,你已經成功走到了 n-2 行(倒數第三行)的某一個位置,那么下一步應該怎么走呢?

   這時,我們就不能通過單純的比大小來決定了。

  但是,我們通過上一步的計算,已經知道了走到 n-1 行的最優解,而且,我們也可以算出來走到 n-1 行某一個位置之后走到的點的和(這里指之前算出的最優解)

  那么問題就很簡單了,只需要走這個值最大的一邊就可以了。

 

  

  重復這個過程。。。

 

  最后:

通過某些神奇的算法,你已經成功走到了第 1 行的某一個位置,那么下一步應該怎么走呢?

  顯然,我們已經知道了下一步該怎么走,,而且這時,我們就可以考慮這個“神奇的算法”了。

  由於第一行只有一個數,所以我們可以肯定的是,我們走到的一定是這個位置。

  最后,再算出第一步該怎么走時,你就算出了這個最優路徑。

 

代碼:

#include <bits/stdc++.h>
using namespace std;

int n, a[1005][1005];

int main(){
    cin >> n;
    for(int i=0; i<n; i++){
        for(int j=0; j<=i; j++){
            cin >> a[i][j];
        }
    }
    for(int i=n-2; i>=0; i--){
        for(int j=0; j<=i; j++){
            a[i][j] += max(a[i+1][j], a[i+1][j+1]);
        }
    }
    cout << a[0][0];

    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM