1. 程式人生 > >[洛谷P1880]石子合併

[洛谷P1880]石子合併

描述
在一個園形操場的四周擺放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; }