[洛谷P1880]石子合併
阿新 • • 發佈:2019-02-15
描述
在一個園形操場的四周擺放N堆石子,現要將石子有次序地合併成一堆.規定每次只能選相鄰的2堆合併成新的一堆,並將新的一堆的石子數,記為該次合併的得分。
試設計出1個演算法,計算出將N堆石子合併成1堆的最小得分和最大得分.
輸入格式:
資料的第1行試正整數N,1≤N≤100,表示有N堆石子.第2行有N個數,分別表示每堆石子的個數.
輸出格式:
輸出共2行,第1行為最小得分,第2行為最大得分.
輸入樣例:
4
4 5 9 4
輸出樣例:
43
54
題解:很經典的問題了,然而我還是隻會記憶化搜尋。。。 記得要有字首和優化。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define LiangJiaJun main
#define INF 1999122700
using namespace std;
int s[1004],a[1004],n,f[1004][1004];
int ld;
int dp1(int i,int j){
if(i == j)f[i][j]=0;
if(f[i][j]<INF) return f[i][j];
for(int k=i;k<j;k++) f[i][j]=min(f[i][j],dp1(i,k)+dp1(k+1 ,j));
return f[i][j]+=s[j]-s[i-1];
}
int dp2(int i,int j){
if(i == j)f[i][j]=0;
if(f[i][j]>=0) return f[i][j];
for(int k=i;k<j;k++) f[i][j]=max(f[i][j],dp2(i,k)+dp2(k+1,j));
return f[i][j]+=s[j]-s[i-1];
}
int LiangJiaJun(){
scanf("%d",&n);
ld=(n<<1)-1;
for (int i=1;i<=n;i++){
int x;
scanf("%d",&x);
a[i]=a[i+n]=x;
}
s[0]=0;
for(int i=1;i<=ld;i++)s[i]=s[i-1]+a[i];
int ans = INF;
for(int i=1;i<=1000;i++)
for(int j=1;j<=1000;j++)f[i][j]=INF;
dp1(1,ld);
for(int i=1;i<=n;i++) ans = min(ans , f[i][i+n-1]);
printf("%d\n",ans);
memset(f,-1,sizeof(f));
dp2(1,ld);
for(int i=1;i<=n;i++) ans = max(ans , f[i][i+n-1]);
printf("%d\n",ans);
return 0;
}