【HDU3480】Division 斜率优化/四边形不等式优化

原题走这里

首先很容易想到给原数组排序,于是最大值和最小值就变成最左和最右。明显是选取连续的区间最优,于是原问题就转化为了区间DP,设前 i i 个中选取j个区间的最优解为 d[i][j] d [ i ] [ j ] ,转移方程:

d[i][j]=min(d[i1][k]+(s[j]s[k])2) d [ i ] [ j ] = m i n ( d [ i − 1 ] [ k ] + ( s [ j ] − s [ k ] ) 2 )
其中s是前缀和
然而复杂度过大,于是我们需要斜率优化。原式展开:
d[i][j]=d[i1][k]+s[j]22s[j]s[k]+s[k]2 d [ i ] [ j ] = d [ i − 1 ] [ k ] + s [ j ] 2 − 2 s [ j ] s [ k ] + s [ k ] 2

移项:
d[i1][k]+s[k]2=2s[j]s[k]+d[i][j]s[j]2 d [ i − 1 ] [ k ] + s [ k ] 2 = 2 s [ j ] ∗ s [ k ] + d [ i ] [ j ] − s [ j ] 2

于是就变成了可以斜率优化的形式:
y=d[i1][k]+s[k]2 y = d [ i − 1 ] [ k ] + s [ k ] 2
x=s[k] x = s [ k ]
k=2s[j] k = 2 ∗ s [ j ]
b=d[i][j]s[j]2 b = d [ i ] [ j ] − s [ j ] 2
单调队列维护一波凸函数就可以啦。具体见代码。

代码如下:

#include <bits/stdc++.h>
using namespace std;
int n,m,t,a[10010],s[10010],d[2][10010],q[10010];
int dp()
{
    for(int i=1;i<=n;i++)
    {
        d[1][i]=(a[i]-a[1])*(a[i]-a[1]);
    }
    for(int i=2,cur=0;i<=m;i++,cur^=1)
    {
        int head=0,tail=0;
        q[tail++]=i-1;
        for(int j=i;j<=n;j++)
        {
            while(head+1<tail)
            {
                int y1=d[cur^1][q[head]]+a[q[head]+1]*a[q[head]+1];
                int x1=a[q[head]+1];
                int y2=d[cur^1][q[head+1]]+a[q[head+1]+1]*a[q[head+1]+1];
                int x2=a[q[head+1]+1];
                if(y1-2*a[j]*x1>=y2-2*a[j]*x2)head++;
                else break;
            }
            d[cur][j]=d[cur^1][q[head]]+(a[j]-a[q[head]+1])*(a[j]-a[q[head]+1]);
//          cout<<i<<' '<<j<<' '<<q[head]<<' '<<d[cur][j]<<' '<<d[cur^1][q[head]]<<endl;
            while(head+1<tail)
            {
                int y1=(d[cur^1][j]+a[j+1]*a[j+1])-(d[cur^1][q[tail-1]]+a[q[tail-1]+1]*a[q[tail-1]+1]);
                int x1=a[j+1]-a[q[tail-1]+1];
                int y2=(d[cur^1][q[tail-1]]+a[q[tail-1]+1]*a[q[tail-1]+1])-(d[cur^1][q[tail-2]]+a[q[tail-2]+1]*a[q[tail-2]+1]);
                int x2=a[q[tail-1]+1]-a[q[tail-2]+1];
                if(y1*x2<=y2*x1)tail--;
                else break;
            }
            q[tail++]=j;
        }
    }
    return d[m&1][n];
}
int main() {
    cin>>t;
    for(int j=1;j<=t;j++)
    {
        cin>>n>>m;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
        }
        sort(a+1,a+n+1);
        printf("Case %d: %d\n",j,dp());
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值