動態規劃 數字三角形
阿新 • • 發佈:2019-01-01
數字三角形 Poj
問題描述
上面給出了一個數字三角形。從三角形的頂部到底部有多條不同的路徑。對於每條路徑,把路徑上面的數字加起來可以得到一個和,累加和最大的路徑成為“最佳路徑”。題目的任務就是求出最佳路徑上的數字之和。
注意:路徑上的每一步只能從一個數走到下一層和它最近的左邊數或者右邊數。
輸入資料
第一行是一個整數N,給出三角形的行數。下面的N行給出數字三角形。數字三角形中的數的範圍都在0~100之間。
輸出要求
輸出最大的和。
輸入樣例
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
輸出樣例
30
遞迴思路
#include <iostream>
#include <algorithm>
#define MAX 101
using namespace std;
int D[MAX][MAX];
int n;
int MaxSum(int i, int j)
{
if(i==n)
return D[i][j];
int x = MaxSum(i+1,j);
int y = MaxSum(i+1,j+1);
return max(x,y)+D[i][j];
}
int main(){
int i,j;
cin >> n;
for(i=1;i<=n;i++)
for(j=1;j<=i;j++)
cin >> D[i][j];
cout << MaxSum(1,1) << endl;
return 0;
}
這道題如果用普通的遞迴來做,可以做出來,但時間複雜度很大,因為中間有很多重複計算,所以效率很低,如果算一個100行的數字三角形,那這個算出這個結果的時間將是不可估量的。
但可以考慮記憶化遞迴來避免重複計算。
每算出一個MaxSum(r,j)就儲存起來,下次用到其值的時候直接取用,這樣可免去重複計算。那麼可以用O(n^2)時間完成計算。因為三角形的數字總 數是 n(n+1)/2
#include<iostream>
#include<algorithm>
using namespace std;
#define MAX 101
int D[MAX][MAX]; int n;
int maxSum[MAX][MAX];
int MaxSum(int i, int j){
if( maxSum[i][j] != -1 )
return maxSum[i][j];
if(i==n)
maxSum[i][j] = D[i][j];
else {
int x = MaxSum(i+1,j); int y = MaxSum(i+1,j+1); maxSum[i][j] = max(x,y)+
D[i][j]; }
return maxSum[i][j];
}
int main(){
int i,j;
cin >> n;
for(i=1;i<=n;i++)
for(j=1;j<=i;j++) {
cin >> D[i][j];
maxSum[i][j] = -1;
}
cout << MaxSum(1,1) << endl;
return 0;
}
動歸思路
"動態規劃"是將一個問題分解為子問題遞迴求解,並且將中間結果儲存以避免重複計算的方法。
而遞迴的思想在程式設計時未必要實現為遞迴函式,可以用遞推的方法來解決。從maxSum[N-1]這一行元素開始向上逐行遞推,最終得到maxSum[0][0]的值,即為最大值。
#include<iostream>
#include<algorithm>
using namespace std;
#define M 100
int d[M][M];
int maxSum[M][M];
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
for(int j=0;j<=i;j++)
{
cin>>d[i][j];
}
}
for(int i=0;i<n;i++)
{
maxSum[n-1][i]=d[n-1][i];
}
for(int i=n-1;i>0;--i)
{
for(int j=0;j<i;j++)
{
if(maxSum[i][j]>sumMax[i][j+1])
maxSum[i-1][j]=sumMax[i][j]+d[i-1][j];
else
maxSum[i-1][j]=sumMax[i][j+1]+d[i-1][j];
}
}
cout<<maxSum[0][0]<<endl;
return 0;
}
還可以再進一步對空間做優化,在用遞推求解過程中,maxSum[i][j]的值在用來計算出maxSum[i-1][j]的值後已經無用,所以可以將計算出的maxSum[i-1][j]的值直接存放在maxSum[i][j]的位置。這樣maxSum不需要是二維陣列,一維陣列就足夠了。
#include<iostream>
#include<algorithm>
using namespace std;
#define M 100
int d[M][M];
int *sumMax;
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
for(int j=0;j<=i;j++)
{
cin>>d[i][j];
}
}
sumMax=d[n-1]; //sumMax指向最後一行
for(int i=n-2;i>=0;i--)
{
for(int j=0;j<=i;j++)
{
sumMax[j]=max(sumMax[j],sumMax[j+1])+d[i][j];
}
}
cout<<sumMax[0]<<endl;
return 0;
}
但時間複雜度仍然是O(N^2)。