zoukankan      html  css  js  c++  java
  • ●BZOJ 4518 [Sdoi2016]征途

    题链:

    http://www.lydsy.com/JudgeOnline/problem.php?id=4518

    题解:

    斜率优化DP

    首先看看最后答案的形式:

    设a[i]为第i天走的距离,那么

    $ANS=frac{sum_{i=1}^{M}(a[i]-overline{x})^2}{M} imes{M^2}$

    $;qquad=frac{(sum_{i=1}^{M}a[i]^2)-2overline{x}SUM+Moverline{x}^2}{M} imes{M^2}$

    $;qquad=M(sum_{i=1}^{M}a[i]^2)-SUM^2$

    由于M和SUM是固定的,所以问题转化为求$sum_{i=1}^{M}a[i]^2$的最小值,

    即把区间分为M段,使得每一段的和的平方加起来最小。

    定义 DP[i][j] 为前i个位置,分为了j段,且i位置为最后一段的结尾的最小值。

    转移:

    $DP[i][j]\,=\,min(DP[k][j-1]+(SUM[i]-SUM[k])^2)$

    然后把式子展开,得到:

    $DP[i][j]\,=\,min(DP[k][j-1]+SUM[k]^2-2SUM[i]SUM[k]+SUM[i]^2)$

    是一个典型的可以用斜率优化的式子。

    (由于DP时是先枚举第二维,一层一层地计算,所以以下的内容中省略掉dp的第二维,同时用g[i]表示上一层的dp[i][~])

    令$Y[j]=g[j]+SUM[j]^2$,

    若对于当前计算的dp[i],存在两个转移来源点 k,j,k < j,且j优于k

    则得到

    $Y[j]-2SUM[i]SUM[j]-Y[k]-2SUM[i]SUM[k]<0$

    化简:$frac{Y[j]-Y[k]}{2SUM[j]-2SUM[k]}<SUM[i]$

    令Slope(j,k)=$frac{Y[j]-Y[k]}{2SUM[j]-2SUM[k]}$,

    则得到结论:若k < j,且Slope(j,k)<SUM[i],则j优于k。

    那么如果存在 k<j<i,且Slope(i,j)<Slope(j,k),则j是无效点,舍去。

    同时注意到SUM[i]单增,所以可以用单调队列维护。

    最终的复杂度 O(N*M)

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define MAXN 3050
    using namespace std;
    int DP[2][MAXN],SUM[MAXN];
    int N,M,*t1=DP[0],*t2=DP[1];
    struct Moque{
    	int q[MAXN],l,r;
    	void Reset(){l=r=1; q[1]=0; t2[0]=0;}
    	double Y(int j){
    		return t2[j]+1.0*SUM[j]*SUM[j];
    	}
    	double X(int j){
    		return 2.0*SUM[j];
    	}
    	double Slope(int j,int k){
    		return (Y(j)-Y(k))/(X(j)-X(k));
    	}
    	void Push(int i){
    		if(l<=r&&SUM[i]==SUM[q[r]]) 
    			{if(t2[i]<t2[q[r]]) r--; else return;}
    		while(l+1<=r&&Slope(i,q[r])<Slope(q[r],q[r-1])) r--;
    		q[++r]=i;
    	}
    	int Query(int i){
    		while(l+1<=r&&Slope(q[l],q[l+1])<SUM[i]) l++;
    		return q[l];
    	}
    }Q;
    int main(){
    	scanf("%d%d",&N,&M);
    	for(int i=1;i<=N;i++)
    		scanf("%d",&SUM[i]),SUM[i]+=SUM[i-1];
    	memset(DP,0x3f,sizeof(DP));
    	t1[0]=0;
    	for(int j=1;j<=M;j++){
    		Q.Reset(); swap(t1,t2);
    		for(int i=1,k;i<=N;i++){
    			Q.Push(i); k=Q.Query(i);
    			t1[i]=t2[k]+(SUM[i]-SUM[k])*(SUM[i]-SUM[k]);
    		}
    	}
    	printf("%d",M*t1[N]-SUM[N]*SUM[N]);
    	return 0;
    }
    

      

  • 相关阅读:
    用numpy实现CNN卷积神经网络
    用numpy实现BP神经网络
    扩展域并查集学习笔记
    My Blog问卷
    二叉搜索树(BST)学习笔记
    我的代码风格
    substr函数学习
    memset与fill的区别
    快读快写
    洛谷题解 CF777A 【Shell Game】
  • 原文地址:https://www.cnblogs.com/zj75211/p/8127176.html
Copyright © 2011-2022 走看看