zoukankan      html  css  js  c++  java
  • 【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
    ",ans);
      }
      
      return 0;
    }
    


  • 相关阅读:
    JavaScript学习-自定义对象/
    网站特效:欢迎窗口/发表评论
    javacript中的事件
    DOM / DOM操作表格
    如何设置文本不换行省略号显示等CSS常用文本属性
    从零开始的H5生活
    Spring手动提交事务
    java基础学习之接口
    java基础学习之抽象类
    java基础学习之final关键字
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793729.html
Copyright © 2011-2022 走看看