簡單的動態規劃,數字三角形,以及作題思路。

連接ios

一句話題目:給出一個n層的三角形,每一個位置有一個數字,到達後可得到,求到達最低層能達到的最大數字和。數組

 

題目分析:優化

首先咱們考慮能不能用搜索作,由於對於一個座標,咱們只有向下的左邊或者右邊。對於一個三角形咱們進行特殊的處理,好比下面的三角形咱們能夠處理成spa

13code

11  8blog

12  7   26遞歸

6   14 15  8get

12 7   13  24 11string

若是咱們到達了一個位置(i,j)咱們繼續向下能夠到達(i+1,j+1)和(i+1,j);io

那麼搜索的代碼:

#include <cstdio>

const int maxn = 1010;

int n, ans, cnt;
int val[maxn][maxn];

void dfs(int i,int j){
    if (i == n+1){
        if (cnt > ans) ans = cnt;
        return ;
    }
    cnt += val[i][j];
    dfs(i+1, j+1);dfs(i+1, j);
    cnt -= val[i][j];
} 
int main(){
    scanf("%d", &n);
    for (int i = 1;i <= n;i ++) 
        for (int j = 1;j <= i;j ++) scanf("%d", &val[i][j]);
    dfs(1, 1);
    printf("%d", ans);
}

複雜度是2^n很明顯超時(題目中n爲1000);

咱們看一個簡單的狀況好比咱們在(i,j)這個位置,這個位置是由(i-1,j)轉移過來,咱們已經求出了全部的(i,j)如下的點咱們向上返回到(i-1,j)這個位置,咱們就向下達到(i,j+1)咱們發現(i,j)能夠到達(i+1,j+1)而且(i,j+1)也能夠到達(i+1,j+1);那麼這個點咱們就計算了兩次,致使效率低下,若是咱們用一個數組表示到達(i,j)能獲得的最大數值,就能夠不用重複計算,這樣就至關於把每一個點都遍歷一遍,複雜度就是((1+n)*n/2) 就不會超時了,咱們把這個方法,叫作記憶化,而整個方法叫作記憶化搜索。

下面看代碼:

#include <cstdio>
#include <cstring>
#include <iostream>

const int maxn = 1010;

using namespace std;

int n;
int val[maxn][maxn], f[maxn][maxn];

int dfs(int i,int j){
    if (f[i][j] != -1) return f[i][j];
    if (i == n+1) return f[i][j] = val[i][j];
    return f[i][j] = max(dfs(i+1, j+1), dfs(i+1, j)) + val[i][j];
}
int main(){
    memset(f, -1, sizeof(f));
    scanf("%d", &n);    
    for(int i = 1;i <= n;i ++)
        for (int j = 1;j <= i;j ++) scanf("%d", &val[i][j]); 
    printf("%d", dfs(1,1));
} 

可是由於遞歸比遞推自己慢因此咱們平時都不寫遞歸,能夠寫成遞推。咱們前面講到的是f[i][j]表示從(i,j)出發能達到的最大值,如今咱們換種方式,用f[i][j]表示從(1,1)出發到達(i,j)的最大值,由於咱們發現(i,j)能夠從(i-1,j)和(i-1,j-1)轉移過來因此咱們按照f[i][j] = max(f[i-1][j-1], f[i-1][j]) + val[i][j];能夠獲得當前點的最大值,那麼由於咱們在計算f[i][j]的時候必定要保證f[i-1][j-1]和f[i-1][j]已經計算過。因此咱們能夠採用順推的方法就是從上向下推。下面看代碼:

#include <cstdio>
#include <cstring>
#include <iostream>

const int maxn = 1010;

using namespace std;

int n, ans;
int val[maxn][maxn], f[maxn][maxn];

int main(){
    scanf("%d", &n);    
    for (int i = 1;i <= n;i ++)
        for (int j = 1;j <= i;j ++) scanf("%d", &val[i][j]);
    for (int i = 1;i <= n;i ++)
        for (int j = 1;j <= i;j ++){
            if (j == 1) f[i][j] = val[i][j] + f[i-1][j];
            else if (j == n) f[i][j] = val[i][j] + f[i-1][j-1];
            else f[i][j] = max(f[i-1][j], f[i-1][j-1]) + val[i][j];
        }
    for (int i = 1;i <= n;i ++) ans = max(ans, f[n][i]);
    printf("%d", ans); 
} 

考慮一個新的思想,從上到下求最大值,能夠轉換爲從最後一排出發到達第一層的最大值,那麼對於(i,j)能夠從(i+1, j+1)和(i+1, j)過來,那麼能夠將整個遞推倒過來寫,就能夠 不用最後一個for循環了。

代碼:

#include <cstdio>
#include <iostream>

const int maxn = 1010;

using namespace std;

int n;
int f[maxn][maxn], val[maxn][maxn];

int main(){
    scanf("%d", &n);
    for (int i = 1;i <= n;i ++)
        for (int j = 1;j <= i;j ++) scanf("%d", &val[i][j]);
    for (int i = n;i >= 1;i --)
        for (int j = 1;j <= i;j ++)
            f[i][j] = max(f[i+1][j], f[i+1][j+1]) + val[i][j];
    printf("%d", f[1][1]);
    return 0;
} 

下面對於動態規劃進行總結:

1.首先定義狀態,考慮有關的變量,而後進行定義

2.找出轉移方程

3.進行優化,通常就是狀態優化和轉移優化。

相關文章
相關標籤/搜索