1. 程式人生 > >洛谷P1880 [NOI1995] 石子合並 [DP,前綴和]

洛谷P1880 [NOI1995] 石子合並 [DP,前綴和]

枚舉 out 兩種 efi font 轉換 解決 輸入格式 HR

  題目傳送門

題目描述

在一個圓形操場的四周擺放N堆石子,現要將石子有次序地合並成一堆.規定每次只能選相鄰的2堆合並成新的一堆,並將新的一堆的石子數,記為該次合並的得分。

試設計出1個算法,計算出將N堆石子合並成1堆的最小得分和最大得分.

輸入輸出格式

輸入格式:

數據的第1行試正整數N,1≤N≤100,表示有N堆石子.第2行有N個數,分別表示每堆石子的個數.

輸出格式:

輸出共2行,第1行為最小得分,第2行為最大得分.

輸入輸出樣例

輸入樣例#1: 復制
4
4 5 9 4

  分析:顯然是用DP。

  由於是環狀,我們需要把它轉換為鏈狀,那麽就把存儲石子的數組a[]空間開大一倍,從n+1~2*n存儲的值等於1~n存儲的值,那麽只需要從1到n枚舉鏈的開頭即可,鏈尾則分別為n到2*n-1。這樣計算和環狀是等效的。

  那麽考慮狀態轉移方程,設d[i][j]是將i~j堆石子合並後得到的最大值,x[i][j]是將i~j堆石子合並後得到的最小值,首先兩重循環i,j枚舉區間的兩端,然後再加一重循環枚舉斷點,也就是說,i~k和k+1~j分別是要合並的兩堆石子,那麽狀態轉移方程不難想到:

  d[i][j]=max(d[i][j],d[i][k]+d[k+1][j]+a[i]+...+a[j])

  x[i][j]=min(x[i][j],x[i][k]+x[k+1][j]+a[i]+...+a[j])

  顯然兩種計算不沖突,可以同時進行,然後a[i]+...+a[j]可以用前綴和優化,那麽這題也就解決了。

  Code:

 1 //It is made by HolseLee on 23rd May 2018
 2 //Luogu.org P1880
 3 #include<bits/stdc++.h>
 4 #define Fi(i,a,b) for(int i=a;i<=b;i++)
 5 #define Fx(i,a,b) for(int i=a;i>=b;i--)
 6 using namespace std;
 7 const int N=1001;
 8 int n,a[N],s[N],d[N][N],x[N][N];
 9 int maxx,minn=0x3f3f3f3f
; 10 int main() 11 { 12 ios::sync_with_stdio(false); 13 cin>>n;Fi(i,1,n){cin>>a[i];a[n+i]=a[i];} 14 Fi(i,1,2*n)s[i]=s[i-1]+a[i]; 15 Fi(c,1,n){ 16 memset(x,0x7f,sizeof(x)); 17 memset(d,0,sizeof(d)); 18 Fi(i,c,c+n-1)d[i][i]=x[i][i]=0; 19 Fx(i,c+n-2,c)Fi(j,i+1,c+n-1)Fi(k,i,j-1){ 20 d[i][j]=max(d[i][j],d[i][k]+d[k+1][j]+s[j]-s[i-1]); 21 x[i][j]=min(x[i][j],x[i][k]+x[k+1][j]+s[j]-s[i-1]);} 22 maxx=max(maxx,d[c][c+n-1]); 23 minn=min(minn,x[c][c+n-1]);} 24 cout<<minn<<"\n"<<maxx;return 0; 25 }

洛谷P1880 [NOI1995] 石子合並 [DP,前綴和]