1. 程式人生 > >6.1 動態規劃:數字三角形

6.1 動態規劃:數字三角形

在上面的數字三角形中尋找一條從頂部到底邊的路徑,使得 路徑上所經過的數字之和最大。路徑上的每一步都只能往左下或 右下走。只需要求出這個最大和即可,不必給出具體路徑。

輸入:

5 7  3 8 8 1 0 2 7 4 4 4 5 2 6 5

輸出:

30 

解析:如果要算第1行第1個數字到底邊的最大和,就要先算第2行第1個數字以及第2行第2個數字到底邊的最大和;

要算第2行第1個數字到底邊的最大和就要第三行第一個和第二個數字到底邊的最大和……很明顯,這是一個典型的遞迴問題。

#include<iostream>
#define N 100
using namespace std;
int a[N][N];
int n;
int SumMax(int i, int j)//第i行第j列的數字到底邊的最大和 
{
	if(i == n)  return a[i][j];
	else
		return max(SumMax(i+1,j),SumMax(i+1,j+1))+a[i][j];
}
int main()
{
	freopen("a.txt","r",stdin);
	cin >> n;
	for(int i = 1; i <= n; ++i)
	{
		for(int j = 1; j <= i; ++j)
		{
			cin >> a[i][j];
		}
	}
	cout << SumMax(1,1);
	return 0;
}

這個程式碼會超時,因為存在大量的重複計算。比如我們從上往下看,在計算第2行第2個數到底邊的最大距離之和時,1這個數字要重複呼叫2次。第2行第一個數字呼叫一次,第2行第2個數字又要呼叫一次。

【改進】 

改進後的程式碼: 

#include<iostream>
#define N 100
using namespace std;
int a[N][N];
int maxsum[N][N];
int n;
int SumMax(int i, int j)//第i行第j列的數字到底邊的最大和 
{
	if(maxsum[i][j] != -1)//要一直呼叫到第n行才會算出結果,然後再出棧算出結果 
		return maxsum[i][j];
	if(i == n)  maxsum[i][j] = a[i][j];
	else
	{
		int x = SumMax(i+1,j);
		int y = SumMax(i+1,j+1);
		maxsum[i][j] = max(x,y)+a[i][j];
	}
	return maxsum[i][j];
}
int main()
{
	freopen("a.txt","r",stdin);
	cin >> n;
	for(int i = 1; i <= n; ++i)
	{
		for(int j = 1; j <= i; ++j)
		{
			cin >> a[i][j];
			maxsum[i][j] = -1;//等於-1說明沒有計算過 
		}
	}
	cout << SumMax(1,1);
	return 0;
} 

 用遞推的方法解決問題:

#include<iostream>
#define N 100
using namespace std;
int a[N][N];
int n; 
int summax[N][N];
int main()
{
	freopen("a.txt","r",stdin);
	cin >> n;
	for(int i = 1; i <= n; ++i)
	{
		for(int j = 1; j <= i; ++j)
		{
			cin >> a[i][j];
		} 
	}
	for(int i = 1; i <= n; ++i)//從最後一行開始遞推 
		summax[n][i] = a[n][i];
	for(int i = n-1; i >= 1; --i)
	{
		for(int j = 1; j <= n; ++j)
		{
			summax[i][j] = max(summax[i+1][j],summax[i+1][j+1]) + a[i][j];
		}
	}
	cout << summax[1][1] << endl;
	return 0;
} 

空間優化:

要點:只需要最後一行記錄每次的和變化即可,因為不需要求出具體的路徑,只需要知道最大和,所以每次用最後一行來更新最大和。

#include<iostream>
#define N 100
using namespace std;
int a[N][N];
int maxsum[N];//求最大和 
int main()
{
	freopen("a.txt","r",stdin);
	int n;
	cin >> n;
	for(int i = 1; i <= n; ++i)
	{
		for(int j = 1; j <= i; ++j)
		{
			cin >> a[i][j];
		}
	}
	for(int i = 1; i <= n; ++i)
	{
		maxsum[i] = a[n][i];
	}
	for(int i = n-1; i >= 1; --i)
	{
		for(int j = 1; j <= n; ++j)
		{
			maxsum[j] = max(maxsum[j],maxsum[j+1]) + a[i][j];
		}
	}
	cout << maxsum[1] << endl;
	return 0;
} 

#include<iostream>
#define N 100
using namespace std;
int a[N][N];
//int maxsum[N];//求最大和 
int *maxsum;
int main()
{
	freopen("a.txt","r",stdin);
	int n;
	cin >> n;
	for(int i = 1; i <= n; ++i)
	{
		for(int j = 1; j <= i; ++j)
		{
			cin >> a[i][j];
		}
	}
	/*
	for(int i = 1; i <= n; ++i)
	{
		maxsum[i] = a[n][i];
	}
	*/
	maxsum = a[n];//maxsum指向第n行 
	for(int i = n-1; i >= 1; --i)
	{
		for(int j = 1; j <= n; ++j)
		{
			maxsum[j] = max(maxsum[j],maxsum[j+1]) + a[i][j];
		}
	}
	cout << maxsum[1] << endl;
	return 0;
}