HDU 3506 Monkey Party(区间dp)

思路:

1.这题是经典区间dp问题“石子合并”的一个变型;
2.“石子合并”是没有围成环的情况,设dp[i][j]为将从ij的石子合成一堆的最小花费(本题即是ij所有猴子认识的花费的时间),则有递推dp[i][j]=min(dp[i][j],d[i][k]+dp[k+1][j]+sum(i,j))其中sum(i,j)ij所有石子的总数目,ki遍历到j-1
3.这种算法的时间复杂度为O(n3)O(n^3),显然有些高,我们可以采用一个数组rcd[i][j]记录遍历ij时的最优的k,那么我们稍微推演一下就可以得知我们每次遍历让krcd[i][j-1]遍历到rcd[i+1][j]即可,但是注意要初始化所有的i<=nrcd[i][i]=i,此时时间复杂度只略大于O(n2)O(n^2)
4.既然这题是环,我们将区间拓展一下,将[1,n-1]区间镜像到n的右边,最后选取花费最小的长度为n的连续区间即可;
(PS:我还有一个想法就是每次遍历一遍数组,选取和最小的相邻猴子,然后用这个和替换这两个猴子,继续遍历,直到只剩一个猴子,这个时间复杂度是O(n2)O(n^2),这个有空再去实现了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<<'
';
}
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;
}
原文地址:https://www.cnblogs.com/yuhan-blog/p/12308784.html