NOI 1995 石子合併
阿新 • • 發佈:2020-10-13
NOI 1995 石子合併
Description
在一個圓形操場的四周擺放著n 堆石子。現要將石子有次序地合併成一堆。規定每次只能選相鄰的2 堆石子合併成新的一堆,並將新的一堆石子數記為該次合併的得分。
試設計一個演算法,計算出將n堆石子合併成一堆的最小得分和最大得分。
Input
包含兩行,第1 行是正整數n(1<=n<=100),表示有n堆石子。
第2行有n個數,分別表示每堆石子的個數。
Output
輸出兩行。
第1 行中的數是最小得分;第2 行中的數是最大得分。
Sample Input
4 4 4 5 9
Sample Output
43 54
題解:
區間DP入門題。
中間需要一個小技巧,斷環成鏈(不是圖論的那個)(大霧
然後在二倍區間上跑一個字首和方便維護每次合併的價值。
需要注意的是DP初態,最小值\(dp1[i][i]\)的初態是0,因為單個無法合併。
所以在二倍區間上區間DP,最後再統計一遍答案即可。
程式碼:
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn=210; const int INF=1e9; int n; int a[maxn],sum[maxn],dp1[maxn][maxn],dp2[maxn][maxn]; int ans1,ans2; int main() { scanf("%d",&n); memset(dp1,0x3f,sizeof(dp1)); ans1=INF; ans2=-INF; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); a[n+i]=a[i]; } for(int i=1;i<=n*2;i++) dp1[i][i]=0; for(int i=1;i<=n*2;i++) sum[i]=sum[i-1]+a[i]; for(int len=2;len<=n;len++) for(int i=1;i<=n*2-len+1;i++) { int j=i+len-1; for(int k=i;k<j;k++) { dp1[i][j]=min(dp1[i][j],dp1[i][k]+dp1[k+1][j]+(sum[j]-sum[i-1])); dp2[i][j]=max(dp2[i][j],dp2[i][k]+dp2[k+1][j]+(sum[j]-sum[i-1])); } } for(int i=1;i<=n;i++) { ans1=min(ans1,dp1[i][i+n-1]); ans2=max(ans2,dp2[i][i+n-1]); } printf("%d\n%d",ans1,ans2); return 0; }