P1880 [NOI1995]石子合並
阿新 • • 發佈:2018-02-10
描述 sin 設計 最小 noi i++ 計算 部分 fin
題目描述
在一個圓形操場的四周擺放N堆石子,現要將石子有次序地合並成一堆.規定每次只能選相鄰的2堆合並成新的一堆,並將新的一堆的石子數,記為該次合並的得分。
試設計出1個算法,計算出將N堆石子合並成1堆的最小得分和最大得分.
輸入輸出格式
輸入格式:
數據的第1行試正整數N,1≤N≤100,表示有N堆石子.第2行有N個數,分別表示每堆石子的個數.
輸出格式:
輸出共2行,第1行為最小得分,第2行為最大得分.
輸入輸出樣例
輸入樣例#1:
4
4 5 9 4
輸出樣例#1:
43
54
解析:
區間dp
dp[i][j]:代表i到j之間能合並出的opt值,k在i到j間滑動,分割為兩個部分
sum打個前綴和就行
狀態轉移方程為: dp[i][j]=opt(dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
#include <bits/stdc++.h> using namespace std; #define maxn 1000 #define inf 0x3f3f3f3f int n,a[maxn],dp1[maxn][maxn],dp2[maxn][maxn]; int sum[maxn]; int ans1=0,ans2=inf; int main() { cin>>n; for(int i=1; i<=n; i++) { cin>>a[i]; a[i+n]=a[i]; } for(int i=1; i<=n<<1; i++) sum[i]=sum[i-1]+a[i]; //讓i作為起點,所以逆著走 for(int i=(n<<1)-1; i>=1; i--) { for(int j=i+1; j<=i+n-1; j++) { dp2[i][j]=inf; for(int k=i; k<j; k++) { dp1[i][j]=max(dp1[i][j],dp1[i][k]+dp1[k+1][j]+sum[j]-sum[i-1]); dp2[i][j]=min(dp2[i][j],dp2[i][k]+dp2[k+1][j]+sum[j]-sum[i-1]); } } } for(int i=1; i<=n; i++) { ans1=max(ans1,dp1[i][i+n-1]); ans2=min(ans2,dp2[i][i+n-1]); } cout<<ans2<<endl<<ans1; return 0; }
P1880 [NOI1995]石子合並