1. 程式人生 > >數字三角形(動態規劃)poj1163

數字三角形(動態規劃)poj1163

問題描述

有一個由非負整陣列成的三角形,第一行只有一個數,除了最下行之外每個數的左下方和右下方各有一個數,從第一行的數開始,每次可以往左下和右下走一格,直到走到最下行,把沿途經過的數全部加起來,如何才能使這個和最大??

狀態轉移方程由來的分析

需要用抽象的方法思考問題:把當前的位置(i,j)看成一個狀態,然後定義狀態(i,j)的指標函式d(i,j)為從格子(i,j)出發時能得到的最大和(包括(i,j)本身的值)。在這個狀態定義下,原問題的解為d(1,1).
從格子(i,j)出發有兩種決策,往左下走或者往右下走,應選擇d(i+1,j),d(i+1,j+1)中較大的那一個,即
d(i,j)=(i,j)+max{d(i+1,j),d(i+1,j+1)}

記憶化搜尋與遞推

!!注意邊界處理:即i=n時

記憶化搜尋

雖然不像遞推法那樣顯示的指明瞭計算順序,但可保證每個節點只訪問一次,時間複雜度為o(n^2)
首先用“menset(d,-1,sizeof(d));”把d全部初始化為-1,然後編寫遞迴函式

<php>
int solve(int i,int j)
{if(d[i][j]!=-1)  return d[i][j];
 return d[i][j]=a[i][j]+(i==n?0:max(solve(i+1,j),solve(i+1,j+1)));
 }

上述程式依然是遞迴的,但同時也把計算結果儲存在陣列d中。題目中說各個數都是非負的,只需把所有d初始化為-1,即可通過判斷得知它是否已經被計算過。

遞推計算

可以用遞推法計算狀態轉移方程。遞推的關鍵是邊界和計算順序。在多數情況下,地推的時間複雜度是狀態總數每個狀態的決策個數決策時間
時間複雜度同記憶化搜尋

int i,j;
for(j=1;j<=n;j++)  d[n][j]=a[n][j];
for(i=n-1;i>=1;i--) 
for(j=1;j<=i;j++)
    d[i][j]=a[i][j]+max(d[i+1][j],d[i+1][j+1]);

可以這樣計算的原因是 i是逆序列舉的,因此在計算d[i][j]前,它所需要的d[i+1][j]和d[i+1][j+1]一定已經計算出來了。

OK來看典型例題

簡單點直接貼程式碼

#include<cstdio>
#include<iostream>
#include<algorithm>
int dp[105][105];
int n;
using namespace std;
int main()
{int i,j;
 while(scanf("%d",&n)!=EOF)
{for(i=1;i<=n;i++)
  {for( j=1;j<=i;j++)
   {scanf("%d",&dp[i][j]);}
  }
 int DP();
 printf("%d\n",DP());
}
return 0;


}

int DP()
{   int i,j;
for(int i=n-1;i>=1;i--)
{   for(j=1;j<=i;j++)
    {
    dp[i][j]+=max(dp[i+1][j],dp[i+1][j+1]);
    }

}
return dp[1][1];

}