1. 程式人生 > >P1880 [NOI1995]石子合並

P1880 [NOI1995]石子合並

描述 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]石子合並