HDU-3506 Monkey Party (环形石子合并)
题目大意:n堆石子围成一圈,每堆石子的块数已知,每次可以将相邻的两堆合并到一堆,块数变为两堆之和,代价也为两堆石子块数之和。求合并到一堆的最小代价。
题目分析:先通过将前n-1依次个移到第n个后面,将环变成线。定义状态dp(i,j)表示将区间(i,j)的石子合并所需的最小代价,则状态转移方程为dp(i,j)=min(dp(i,k)+dp(k+1,j)+sum(i,j))。时间复杂度为O(n*n*n),利用四边形不等式优化,限制k(i,j)的取值范围在k(i,j-1)~k(i+1,j)之间,达到优化效果。
代码如下:
# include<iostream> # include<cstdio> # include<cstring> # include<algorithm> using namespace std; const int INF=1<<30; const int N=1005; int n; int dp[N<<1][N<<1]; int s[N<<1][N<<1]; int sum[N<<1]; int a[N<<1]; void init() { sum[0]=0; for(int i=1;i<=n;++i){ scanf("%d",a+i); sum[i]=a[i]+sum[i-1]; } for(int i=1;i<n;++i){ a[n+i]=a[i]; sum[n+i]=a[n+i]+sum[n+i-1]; } } void solve() { for(int l=1;l<=n;++l){ for(int i=0;i+l-1<2*n;++i){ int j=i+l-1; if(l==1){ dp[i][i]=0; s[i][i]=i; }else{ dp[i][j]=INF; for(int k=s[i][j-1];k<=s[i+1][j];++k){ if(dp[i][j]>dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]){ dp[i][j]=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]; s[i][j]=k; } } } } } int ans=INF; for(int i=1;i<=n;++i) ans=min(ans,dp[i][n+i-1]); printf("%d\n",ans); } int main() { while(~scanf("%d",&n)) { init(); solve(); } return 0; }