HDU 3506 Monkey Party(区间dp)
思路:
1.这题是经典区间dp问题“石子合并”的一个变型;
2.“石子合并”是没有围成环的情况,设dp[i][j]
为将从i
到j
的石子合成一堆的最小花费(本题即是i
到j
所有猴子认识的花费的时间),则有递推dp[i][j]=min(dp[i][j],d[i][k]+dp[k+1][j]+sum(i,j))
其中sum(i,j)
是i
到j
所有石子的总数目,k
从i
遍历到j-1
;
3.这种算法的时间复杂度为,显然有些高,我们可以采用一个数组rcd[i][j]
记录遍历i
到j
时的最优的k
,那么我们稍微推演一下就可以得知我们每次遍历让k
从rcd[i][j-1]
遍历到rcd[i+1][j]
即可,但是注意要初始化所有的i<=n
,rcd[i][i]=i
,此时时间复杂度只略大于;
4.既然这题是环,我们将区间拓展一下,将[1,n-1]
区间镜像到n
的右边,最后选取花费最小的长度为n
的连续区间即可;
(PS:我还有一个想法就是每次遍历一遍数组,选取和最小的相邻猴子,然后用这个和替换这两个猴子,继续遍历,直到只剩一个猴子,这个时间复杂度是,这个有空再去实现了orz)
代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int INF=1<<30;
const int MAX_N=2005;
int n,m[MAX_N],sum[MAX_N],dp[MAX_N][MAX_N],rcd[MAX_N][MAX_N];
void solve(){
int N=2*n-1;
for(int len=1;len<n;len++)
for(int i=1;i<=N-len;i++){
int j=i+len;
dp[i][j]=INF;
for(int k=rcd[i][j-1];k<=rcd[i+1][j];k++)
if(dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]<dp[i][j]){
dp[i][j]=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
rcd[i][j]=k;
}
}
int ans=dp[1][n];
for(int i=2;i<=n;i++) ans=min(ans,dp[i][i+n-1]);
cout<<ans<<'\n';
}
void clear(){
for(int i=0;i<=2*n;i++){
dp[i][i]=0; rcd[i][i]=i;
for(int j=i+1;j<=2*n;j++)
dp[i][j]=dp[j][i]=rcd[i][j]=rcd[j][i]=0;
}
}
int main(){
while(cin>>n){
for(int i=1;i<=n;i++) cin>>m[i],sum[i]=sum[i-1]+m[i];
for(int i=1;i<n;i++) sum[n+i]=sum[n+i-1]+m[i];
clear(); solve();
}
return 0;
}