【HDU2829】Lawrence-DP+四边形不等式优化+前缀和

测试地址:Lawrence

题目大意:一段铁路有N个节点,之间有N-1条边连成一条链,每个点有一个策略值,策略值都是正整数。现在要炸掉M条边,使得铁路的总策略值最小。总策略值是这样计算的:考虑每一个无序点对,如果该点对两点之间连通,那么就在总策略值中加上这两点的策略值的乘积。

做法:炸掉M条边就是把原铁路分成M+1段,使得每段内点的策略值两两相乘之和最小。我们设f(i,j)为前j个节点构成的铁路划分成i段的最小总策略值,w(i,j)为从点i到点j一段的总策略值,那么状态转移方程很容易想到是:

f(i,j)=min{f(i-1,k-1)+w(k,j)} (i≤k≤j)

如果用最暴力的方法,每次计算w都要耗费O(N^2)的时间,那么上式就需要非常恐怖的O(N^5)的时间,显然爆炸。我们先从w的计算入手进行优化。

考虑到w(i,j)=ΣAxAy (i≤x<y≤j),而(Ai+...+Aj)^2=ΣAxAy (i≤x,y≤j),于是可以得到:

w(i,j)=((Ai+...+Aj)^2-(Ai^2+...+Aj^2))/2

这样每次求w就只用O(N)的时间了。然而还是不够,我们发现式子中的若干连续项的和与若干连续项的平方和是可以用前缀和预先处理的,所以设sum(i)=A1+...+Ai,sqsum(i)=A1^2+...+Ai^2,将原式化为:

w(i,j)=((sum(j)-sum(i-1))^2-(sqsum(j)-sqsum(i-1)))/2

则一开始需要O(N)的预处理,之后每次计算w就只用O(1)的时间,那么原状态转移方程就是O(N^3)的了。然而我们发现还是不够优,而w已经显然没有优化的空间了,那么我们就从状态转移本身进行考虑。注意到状态转移方程是非常典型的区间型DP,自然想到用四边形不等式优化,那么我们只需证明w满足区间包含单调性和四边形不等式即可。区间包含单调性根据定义是显然的,四边形不等式的话就展开来证,最后推出和HDU3480差不多的式子就可以证出来了。于是状态转移方程优化到O(N^2),可以通过全部数据。

以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m;
long long inf,sum[1010],sqsum[1010],f[2][1010],s[2][1010];

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

int main()
{
  inf=2000000000;
  inf*=1000000000;
  
  while(scanf("%d%d",&n,&m)&&n)
  {
    m++;
	sum[0]=sqsum[0]=0;
	for(int i=1,a;i<=n;i++)
	{
	  scanf("%d",&a);
	  sum[i]=sum[i-1]+a;
	  sqsum[i]=sqsum[i-1]+a*a;
	}
	
	int past=0,now=1;
	for(int i=1;i<=n;i++) f[past][i]=inf,s[past][i]=1;
	f[past][0]=0;
	for(int i=1;i<=m;i++)
	{
	  s[now][n+1]=n;
	  for(int j=n;j>=i;j--)
	  {
	    f[now][j]=inf;
	    for(int k=s[past][j];k<=s[now][j+1];k++)
		  if (f[now][j]>=f[past][k-1]+getw(k,j))
		  {
		    f[now][j]=f[past][k-1]+getw(k,j);
			s[now][j]=k;
		  }
	  }
	  swap(past,now);
	}
	
	printf("%lld
",f[past][n]);
  }
  
  return 0;
}


原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793731.html