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;
    }
    


  • 相关阅读:
    sublime text 4 vim 插件配置
    ssh-keygen 的使用
    distribution transaction solution
    bilibili 大数据 视频下载 you-get
    Deepin 20.2.1 安装 MS SQL 2019 容器版本
    【转】使用Linux下Docker部署MSSQL并加载主机目录下的数据库
    【转】You Can Now Use OneDrive in Linux Natively Thanks to Insync
    dotnet 诊断工具安装命令
    Linux 使用 xrandr 设置屏幕分辨率
    【转】CentOS 7.9 2009 ISO 官方原版镜像下载
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793730.html
Copyright © 2011-2022 走看看