【HDU3506,HOJ2952】Monkey Party-环状区间合并DP+四边形不等式优化

测试地址:Monkey Party

题目大意:N只猴子坐成一圈,每只猴子有一个介绍时间,一开始每只猴子之间互不认识(除了自己认识自己),现在要对它们进行介绍使得它们之间互相认识,每一次介绍是发生在相邻的两只猴子之间的,每次介绍完后,两只猴子所认识的所有猴子都会互相认识,花费的时间为两只猴子所认识的所有猴子的介绍时间之和,求介绍所有猴子认识的最小时间。

做法:注意到认识关系绝对不会“跨越”,即相互认识的猴子一定在环上连续的一段上,这就启示我们想到区间合并型DP。设f(i,j)为介绍从第i到第j只猴子认识的最小时间,w(i,j)为第i到第j只猴子的总介绍时间,则状态转移方程为:f(i,j)=min{f(i,k-1)+f(k,j)+w(i,j)} (i<k≤j)。可是这一题是环状的,环上的区间合并要怎么做呢?我们可以看做先把环的某一处割开形成一条链,然后再在链上做区间合并,最后把这些结果相比对求出最小值即可。具体的做法是:在第n只猴子后面再加入n只猴子,其中新加入的第i只猴子的信息和原来的第i只猴子信息相同,这样的话新的数列上的区间[1,n],[2,n+1],...,[n,2*n-1]就一一对应了在原来环上切割所得到的链,所以答案就是min{f(i,i+n-1)} (1≤i≤n)。

然而又回到状态转移方程上来,我们发现暴力做这个方程的复杂度是O(N^4)的,而N达到1000,显然无法通过。我们发现实际上w(i,j)就是从i到j一段的和,这个我们可以用前缀和处理,即用一个数组sum存储从1到i一段的和,所以w(i,j)=sum[j]-sum[i-1],方程降了一维。然而还是不够,我们看这个状态转移方程的形式,感觉它能进行四边形不等式优化,实际上就是可以,我们只需要证明w(i,j)满足区间包含单调性和四边形不等式即可。w满足区间包含单调性由w的定义来看是显然的,要证明w满足四边形不等式即证明对于任意i≤i'<j≤j',w(i,j)+w(i',j')≤w(i,j')+w(i',j),我们把式子展开之后发现左右都是sum[j]+sum[j']-sum[i-1]-sum[i'-1],两边相等,定理得证。至于后面证明f满足四边形不等式看这里,里面所讲的第一个例子就是这个方程。于是原状态转移方程复杂度降到O(N^2),可以通过全部数据。

以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,sum[2010],s[2010][2010],f[2010][2010];

int getw(int i,int j)
{
  return sum[j]-sum[i-1];
}

int main()
{
  while(scanf("%d",&n)!=EOF)
  {
    sum[0]=sum[n]=0;
    for(int i=1,a;i<=n;i++)
	{
	  scanf("%d",&a);
	  sum[i]=sum[i-1]+a;
	  sum[i+n]=sum[i+n-1]+a;
	}
	for(int i=1;i<=n;i++)
	  sum[i+n]+=sum[n];
	
	memset(f,0,sizeof(f));
	for(int i=1;i<=2*n;i++) s[i][i]=i;
	for(int l=2;l<=n;l++)
	  for(int i=1;i+l-1<=2*n;i++)
	  {
	    int j=i+l-1;
		f[i][j]=1000000000;
		for(int k=s[i][j-1];k<=s[i+1][j];k++)
		  if (i<k&&f[i][j]>=f[i][k-1]+f[k][j]+getw(i,j))
		  {
		    f[i][j]=f[i][k-1]+f[k][j]+getw(i,j);
			s[i][j]=k;
		  }
	  }
	
	int ans=1000000000;
	for(int i=1;i<=n;i++)
	  ans=min(ans,f[i][i+n-1]);
	printf("%d\n",ans);
  }
  
  return 0;
}


posted @ 2017-04-19 16:47  Maxwei_wzj  阅读(119)  评论(0编辑  收藏  举报