題目:
在乙個圓形操場的四周擺放 n 堆石子,現要將石子有次序地合併成一堆.規定每次只能選相鄰的2堆合併成新的一堆,並將新的一堆的石子數,記為該次合併的得分。
試設計出乙個演算法,計算出將 n堆石子合併成 1堆的最大得分。
輸入格式
資料的第 1行是正整數 n,表示有 n 堆石子。
第 2行有 n 個整數,第 i 個整數 ai 表示第 i 堆石子的個數。
輸出格式
1 行為最大得分
輸入樣例
713 7 8 16 21 4 18
輸出樣例
376演算法思想:
環形結構上的動態規劃問題
在許多環形結構的問題中,我們都能夠通過列舉法,選擇乙個位置把環斷開,變成
線性結構進行計算,最後根據每次列舉的結果求出答案。我們把能通過上述列舉方式求解環形問題稱為「可拆解的環形問題」,這也是本文章的主要研究物件。我們的目標是採取適當的策略避免列舉,從而使時間複雜度得到降低。
第一種策略:
執行兩側dp,第一次在任意位置把環斷開成鏈,按照線性問題求解;第二次通過適當的條件和賦值,保證計算出的狀態等價於斷開位置強制相連。
第二種策略:在任意位置斷開成鏈,然後複製一倍接在末尾。這是解決環形結構dp常用手法。
假設石頭為a1,a2,a3…an,首尾相連之後就是 a1,a2,a3…an,a1,a2,a3…an;序列長度變為原來的2倍。
a1,a2,a3…an,a1,a2,a3…an;
這樣和原來的線性石子合併一樣了,列舉區間長度2<= len<=n,列舉區間起點,這個有點區別,列舉範圍是1<= i <=2n
dp[ i ][ j ]=max(dp[ i ][ j ],dp[ i ][ k ]+dp[k+1][ j ]+(sum[ j ]-sum[ i-1 ]));
求區間終點 int j=i+len-1; if(j>n*2) break ; 越界結束
然後列舉環裝中的每乙個數為起點,長度為n的區間,求最大值即可。
for(int i=1;i<=n;i++)//列舉環狀序列的起點,長度為n
max=max(dp[i][i+n-1],max);//求最大值
完整**:
#include
using
namespace std;
const
int maxn=
450;
//區間長度為2*n
int dp[maxn]
[maxn]
;int sum[maxn]
;//字首和陣列
int a[maxn]
;//每堆石子的個數
intmain()
int in=1;
//首尾相連之後
for(
int i=n+
1;i<=
2*n;i++
) sum[i]+=
(sum[i-1]
+a[in++])
;for
(int len=
2;len<=n;len++
)//列舉區間長度}}
int max=-1
;for
(int i=
1;i<=n;i++
)//列舉環狀序列的起點,長度為n
max=
max(dp[i]
[i+n-1]
,max)
;//求最大值
printf
("%d\n"
,max);}
return0;
}
環形石子合併問題
環形石子合併問題是在普通的相鄰石子合併問題的基礎上稍加拓展,石子變成了環形的,也就是說每個石子都可能和其左右兩邊的石子合併。那麼它的dp解法也是基於普通的相鄰石子合併問題,不了解的同學可以參考我寫過的這篇文章。有兩種解法,但他們基本的演算法思想是一樣的。第一種是和上面鏈結文章的寫法類似,不同的是對於...
環形區間DP 環形石子合併
在乙個園形操場的四周擺放n堆石子,現要將石子有次序地合併成一堆.規定每次只能選相鄰的2堆合併成新的一堆,並將新的一堆的石子數,記為該次合併的得分。試設計出1個演算法,計算出將n堆石子合併成1堆的最小得分和最大得分.輸入輸出格式 輸入格式 資料的第1行試正整數n,1 n 100,表示有n堆石子.第2行...
石子合併 動態規劃(環形)
1 問題描述 問題 nwpu noj 1148 在乙個圓形操場的四周擺放著n堆石子 n 100 現要將石子有次序地合併成一堆。規定每次只能選取相鄰的兩堆合併成新的一堆,並將新的一堆的石子數,記為該次合併的得分。編一程式,讀入石子堆數n及每堆的石子數 20 選擇一種合併石子的方案,使得做n 1次合併,...