HDU 3480 Division 斜率DP

Division HDU - 3480

TT 表示一个整数的集合,MIN,MAXMIN,MAX 分别是这个集合中最小和最大的数,定义这个集合的花费为 (MAXMIN)2(MAX-MIN)^2

先给定一个整数集合 SS,希望找到 MMSS 的子集 S1,S2,SMS_1,S_2,\cdots S_M,使得 S1S2SM=SS_1\cup S_2\cup\cdots\cup S_M=S,并且整体的花费最小。

要使花费小的话肯定要保证集合中最大值和最小值的差较小。首先对元素进行排序,然后用 dp[i][j]dp[i][j] 表示前 ii 个数分成 jj 个集合的最小花费,递推方程为:

dp[i][j]=min{dp[k][j1]+(a[i]a[k+1])2} dp[i][j]=\min\{dp[k][j-1]+(a[i]-a[k+1])^2\}

还需要考虑的是两个子集之间的元素有可能重复,但是可以证明,子集尽量不相交时可使花费最小:假如子集够多,那么每个元素单独成一个子集,这样的花费是最小的。

再设 k1<k2k_1<k_2k2k_2 优于 k1k_1,则:

dp[k2][j1]+(a[i]a[k2+1])2<dp[k1][j1]+(a[i]a[k1+1])2dp[k2][j1]+a2[k2+1](dp[k1][j1]+a2[k1+1])<2a[i](a[k2+1]a[k1+1])yk2yk1xk2xk1<2a[i] \begin{aligned} dp[k_2][j-1]+(a[i]-a[k_2+1])^2&<dp[k_1][j-1]+(a[i]-a[k_1+1])^2\\ dp[k_2][j-1]+a^2[k_2+1]-(dp[k_1][j-1]+a^2[k_1+1])&<2a[i](a[k_2+1]-a[k_1+1])\\ \frac{y_{k_2}-y_{k_1}}{x_{k_2}-x_{k_1}}&<2a[i] \end{aligned}

其中 yk=dp[k][j1]+a2[k+1],xk=a[k+1]y_k=dp[k][j-1]+a^2[k+1],x_k=a[k+1]a[i]a[i] 为排序后第 ii 个元素的值。

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
//#define WINE
#define MAXN 10010
using namespace std;
int T,iCase,n,m,a[MAXN],h,t,q[MAXN],dp[MAXN][MAXN];
int up(int k2,int k1,int j){
    return dp[k2][j-1]+a[k2+1]*a[k2+1]-(dp[k1][j-1]+a[k1+1]*a[k1+1]);
}
int down(int k2,int k1){
    return a[k2+1]-a[k1+1];
}
int getDP(int k,int i,int j){
    return dp[k][j-1]+(a[i]-a[k+1])*(a[i]-a[k+1]);
}
int main(){
#ifdef WINE
    freopen("data.in","r",stdin);
#endif
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        sort(a+1,a+1+n);
        for(int i=1;i<=n;i++)dp[i][1]=(a[i]-a[1])*(a[i]-a[1]);
        for(int j=2;j<=m;j++){
            h=t=0;q[t++]=j-1;
            for(int i=j;i<=n;i++){
                while(h+1<t&&up(q[h+1],q[h],j)<2*a[i]*down(q[h+1],q[h]))
                    h++;
                dp[i][j]=getDP(q[h],i,j);
                while(h+1<t&&up(i,q[t-1],j)*down(q[t-1],q[t-2])<=up(q[t-1],q[t-2],j)*down(i,q[t-1]))
                    t--;
                q[t++]=i;
            }
        }
        printf("Case %d: %d\n",++iCase,dp[n][m]);
    }
    return 0;
}

在这里插入图片描述

posted @ 2020-03-24 16:59  winechord  阅读(84)  评论(0编辑  收藏  举报