HDU.1003 Max Sum

原題

HDU.1003 Max Sum

分類

動態規劃

題意

計算從一個序列中最大連續子序列和、對應的起始元素和終止元素的位置。

輸入/輸出 要求與格式
樣例數的確定 最開始一行開始輸入樣例數
每個樣例的輸入 每行n + 1個數,第一個數n爲數組元素個數,後n個數爲序列的實際元素
輸出結果 最大連續子序列和、對應的起始元素和終止元素的位置
輸出格式 每個案例結果第一行爲"Case X:",第二行爲3個輸出結果(空格隔開),兩個案例結果間一個空行
特殊要求 有多個解的案例使用最前面的解

以下是數據範圍:

數據 數據範圍
樣例數 1T201 \leq T \leq 20
序列元素個數 1n1051 \leq n \leq 10^5
序列元素 103ai103-10^3 \leq a_i \leq 10^3

題解

這道題規定了數組元素個數最大可能有10510^5個。

如果出於最暴力的方法來考慮,嘗試每一種子序列的和,用前綴和的方法來優化求和,時間複雜度爲O(n2)O \left( n^2 \right),這樣的計算方法肯定會TLE啊,必須換個思路。

其實在學動態規劃的時候,我們老師講的第一道例題就是這道,以下是動態規劃的思路表格:

\ 具體內容
dp[x]dp \left[ x \right] 序列前xx個數中,必須包含a[x]a \left[ x \right]的最大連續序列和
初始狀態 dp[1]=a[1]dp \left[ 1 \right] = a \left[ 1 \right]
狀態轉移方程 dp[x]={dp[x1]+a[x]dp[x1]0a[x]dp[x1]<0dp \left[ x \right] = \begin{cases} dp \left[ x-1 \right] + a\left[ x \right] & {dp\left[ x-1 \right] \geq 0}\\ a \left[ x \right] & {dp\left[ x-1 \right] < 0} \end{cases}
計算結果 max1indp[i]\max \limits_{1 \leq i \leq n} dp \left[ i \right]

再來分析一下這個動態規劃的合理性。

1、dp[x]dp \left[ x \right]的設計
最大連續子序列和必然是序列的前ii個數中、以某個a[i]a \left[ i \right]結尾的一個序列。

2、狀態轉移方程
我們很容易看出,在這種dp[x]dp \left[ x \right]的設計下,爲了使dp[x]dp \left[ x \right]能夠達到最大值,dp[x]dp \left[ x \right]的計算只有兩種選擇:

  • ①併入前面dp[x1]dp \left[ x-1 \right]對應的序列,繼續拼接形成新的連續子序列。
  • ②捨棄前面dp[x1]dp \left[ x-1 \right]對應的序列,自己本身單獨形成一個新的連續子序列。

這兩種方式我們要找到一個值最大的,也就是dp[x]=max{dp[x1]+a[x],a[x]}dp \left[ x \right] = \max \left\{ dp \left[ x-1 \right]+a \left[ x \right] , a \left[ x \right] \right\},其實和上面表格中列出的公式是一個意思,可能顯得簡化一些。

3、初始狀態
最初始的狀態很顯然就是前1個數中必須包含a[1]a \left[ 1 \right]的最大連續子序列和,dp[1]=a[1]dp \left[ 1 \right] = a \left[ 1 \right].

4、計算結果
最終的結果就是所有dp[x1]dp \left[ x-1 \right]中,值最大而且還靠前的那組解。

對於位置信息,我們也可以另外寫上一個start數組,用start[x]\text{start} \left[ x \right]來標記dp[x]dp \left[ x \right]對應序列的起始位置。每個案例的輸出結果依次爲dp[x]dp \left[ x \right]start[x]\text{start} \left[ x \right]xx.

對於格式要求,兩個案例間一個空行,可以理解爲最後一個案例後不加空行。因此,只要在打印時,根據案例計數器作一個判斷就可以了。

題解代碼

HDU(C++/G++)AC代碼如下:

#include <iostream>
#include <algorithm>

using namespace std;

int a[100005];
int dp[100005];
int start[100005];

int main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

	//數據準備
	int T;
	int cnt = 1;
	int n;
	//樣例數
	cin >> T;
	while (T--)
	{
		//接收輸入
		cin >> n;
		for (int i = 1; i <= n; ++i)
			cin >> a[i];
		//初始化狀態
		dp[1] = a[1];
		start[1] = 1;
		//狀態轉移,開始dp
		//狀態轉移方程:
		//    dp[x] = dp[x - 1] + a[x](dp[x - 1] >= 0)
		//    dp[x] = a[x](dp[x - 1] < 0)
		for (int i = 2; i <= n; ++i)
		{
			if (dp[i - 1] >= 0)
			{
				dp[i] = dp[i - 1] + a[i];
				start[i] = start[i - 1];
			}
			else
			{
				dp[i] = a[i];
				start[i] = i;
			}
		}
		//搜素結果
		int maxt = 1;
		for (int i = 2; i <= n; ++i)
			if (dp[i] > dp[maxt])
				maxt = i;
		//輸出結果
		cout << "Case " << cnt++ << ':' << endl;
		cout << dp[maxt] << ' ' << start[maxt] << ' ' << maxt << endl;
		if (T)
			cout << endl;
	}

	return 0;
}

評價

這道題算是一道動態規劃(dp)的入門題,要想熟練使用dp,還是得多刷題長長見識啊。

發佈了4 篇原創文章 · 獲贊 4 · 訪問量 300
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章