【斜率/四边形不等式DP】【hdu 2829】Lawrence

时间:2023-01-03 16:10:35

http://acm.hdu.edu.cn/showproblem.php?pid=2829


给你一个序列A,请你把序列A分成连续K个子段,每个子段的代价是 sum(A[i]*A[j]) 其中 i < j。请问如何分组使代价最小。
    数据范围|A|,K <1000

这道题用四边形和斜率都可以


四边形很简单,不多说,直接套(chao)模板

//#define _TEST _TEST
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
/************************************************
Code By willinglive    Blog:http://willinglive.cf
************************************************/
#define rep(i,l,r) for(int i=l,___t=(r);i<=___t;i++)
#define per(i,r,l) for(int i=r,___t=(l);i>=___t;i--)
#define MS(arr,x) memset(arr,x,sizeof(arr))
#define LL long long
#define INE(i,u,e) for(int i=head[u];~i;i=e[i].next)
inline const int getint()
{
    int r=0,k=1;char c=getchar();
    for(;c<'0'||c>'9';c=getchar())if(c=='-')k=-1;
    for(;c>='0'&&c<='9';c=getchar())r=r*10+c-'0';
    return k*r;
}
/////////////////////////////////////////////////
int n,m;
int a[1010],sum[1010];
LL dp[1010][1010],s[1010][1010],cost[1010][1010];
/////////////////////////////////////////////////
/////////////////////////////////////////////////
void input()
{
	m++;
    rep(i,1,n) a[i]=getint(),sum[i]=sum[i-1]+a[i];
}
void solve()
{
	MS(dp,0x3f);
	rep(i,1,n) rep(j,i,n) cost[i][j]=cost[i][j-1]+(sum[j-1]-sum[i-1])*a[j];
	rep(i,0,m) dp[0][i]=0;
	rep(i,1,n) dp[i][1]=cost[1][i],s[i][1]=0;
	rep(j,1,m)
	{
		s[n+1][j]=n;
		per(i,n,j)
		{
			rep(k,s[i][j-1],s[i+1][j])
			{
				if(dp[i][j]>dp[k][j-1]+cost[k+1][i])
				{
					s[i][j]=k;
					dp[i][j]=dp[k][j-1]+cost[k+1][i];
				}
			}
		}
	}
    LL ans=(((unsigned long long)0)-1)>>1;
    rep(i,1,m) ans=min(ans,dp[n][i]);
    cout<<ans<<endl;
}
/////////////////////////////////////////////////
int main()
{
    #ifndef _TEST
    freopen("std.in","r",stdin); freopen("1.out","w",stdout);
    #endif
    while(scanf("%d%d",&n,&m),n+m)
    {
    input();
    solve();
    }
    return 0;
}

斜率,我的草稿:

dp[i][j]=min{dp[k][j-1]+cost[k+1][i]}


sum[i][j]^2=(a[i]+...+a[j])^2=sum2[i][j]+2cost[i][j]
cost[i][j]=(sum[i][j]^2-sum2[i][j])/2


dp[i][j]=min{dp[k][j-1]+(sum[k+1][i]^2-sum2[k+1][i])/2}
dp[i][j]=min{dp[k][j-1]+(s[i]^2-2*s[i]*s[k]+s[k]^2-s2[i]+s2[k])/2}
x<y,x比y优
dp[x][j-1]+(s[i]^2-2*s[i]*s[x]+s[x]^2-s2[i]+s2[x])/2<dp[y][j-1]+(s[i]^2-2*s[i]*s[y]+s[y]^2-s2[i]+s2[y])/2
dp[x][j-1]+(-2*s[i]*s[x]+s[x]^2+s2[x])/2<dp[y][j-1]+(-2*s[i]*s[y]+s[y]^2+s2[y])/2
dp[x][j-1]-s[i]*s[x]+s[x]^2/2+s2[x]/2<dp[y][j-1]-s[i]*s[y]+s[y]^2/2+s2[y]/2
s[i]<(dp[y][j-1]+s[y]^2/2+s2[y]/2-dp[x][j-1]-s[x]^2/2-s2[x]/2)/(s[y]-s[x])
s[i]<(dp[y][j-1]+s[y]^2/2+s2[y]/2) - dp[x][j-1]+s[x]^2/2+s2[x]/2)) / (s[y] - s[x])
Y[k]=dp[k][j-1]+s[k]^2/2+s2[k]/2
X[k]=s[k]
s[i]<(Y[y]-Y[x]) / (X[y]-X[x])

满足条件小的优


斜率还有一种方法:http://hi.baidu.com/adrnpduvzbjnpsr/item/0af88fb33b03a3fb62388eda

基本思想也是转化为前缀和

//#define _TEST _TEST
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
/************************************************
Code By willinglive    Blog:http://willinglive.cf
************************************************/
#define rep(i,l,r) for(int i=l,___t=(r);i<=___t;i++)
#define per(i,r,l) for(int i=r,___t=(l);i>=___t;i--)
#define MS(arr,x) memset(arr,x,sizeof(arr))
#define LL long long
#define INE(i,u,e) for(int i=head[u];~i;i=e[i].next)
inline const int getint()
{
    int r=0,k=1;char c=getchar();
    for(;c<'0'||c>'9';c=getchar())if(c=='-')k=-1;
    for(;c>='0'&&c<='9';c=getchar())r=r*10+c-'0';
    return k*r;
}
/////////////////////////////////////////////////
int n,m;
int a[1001];
LL s[1001],s2[1001];
LL dp[1001][1001];
int q[1010],l,r,j;
/////////////////////////////////////////////////
inline LL sqr(LL x){return x*x;}
LL X(int k){return s[k];}
LL Y(int k){return dp[k][j-1]+(sqr(s[k])+s2[k])/2;}
double slope(int k,int j){return (double)(Y(k)-Y(j))/(X(k)-X(j));}
/////////////////////////////////////////////////
void input()
{
	m++;
    rep(i,1,n) a[i]=getint(),s[i]=s[i-1]+a[i],s2[i]=s2[i-1]+sqr(a[i]);
}
void solve()
{
	rep(i,1,n) dp[i][1]=(sqr(s[i])-s2[i])/2;
    for(j=2;j<=m;j++)
    {
    	l=r=0;
    	rep(i,1,n)
    	{
    		while(l<r&&slope(q[l],q[l+1])<s[i])l++;
    		int k=q[l]; dp[i][j]=dp[k][j-1]+(sqr(s[i]-s[k])-(s2[i]-s2[k]))/2;
    		while(l<r&&slope(q[r],q[r-1])>slope(i,q[r]))r--;
    		q[++r]=i;
    	}
    }
    cout<<dp[n][m]<<endl;
}
/////////////////////////////////////////////////
int main()
{
    #ifndef _TEST
    freopen("std.in","r",stdin); freopen("2.out","w",stdout);
    #endif
    while(scanf("%d%d",&n,&m),n+m)
    {
    input();
    solve();
    }
    return 0;
}