zoukankan      html  css  js  c++  java
  • [洛谷P4072] SDOI2016 征途

    问题描述

    Pine开始了从S地到T地的征途。

    从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站。

    Pine计划用m天到达T地。除第m天外,每一天晚上Pine都必须在休息站过夜。所以,一段路必须在同一天中走完。

    Pine希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小。

    帮助Pine求出最小方差是多少。

    设方差是v,可以证明,(v imes m^2)是一个整数。为了避免精度误差,输出结果时输出(v imes m^2)

    输入格式

    第一行两个数 n、m。

    第二行 n 个数,表示 n 段路的长度

    输出格式

    一个数,最小方差乘以 (m^2) 后的值 。

    样例输入

    5 2
    1 2 5 8 6

    样例输出

    36

    说明

    对于 (30\%) 的数据,(1 le n le 10)

    对于 (60\%) 的数据,(1 le n le 100)

    对于 (100\%) 的数据,(1 le n le 3000)

    保证从 S 到 T 的总路程不超过 30000 。

    解析

    首先,我们需要化简方差的式子,

    [egin{align}s^2 &=frac{sum_{i=1}^{m}(overline v-v_i)^2}{m}\ &=frac{moverline v^2-2overline v(v_1+v_2+...+v_m)+(v_1^2+v_2^2+...+v_m^2)}{m}\ &=frac{mfrac{(sum_{i=1}^{m}v_i)^2}{m^2}-2frac{(sum_{i=1}^{m}v_i)^2}{m}+v_1^2+v_2^2+...+v_m^2}{m}\end{align} ]

    所以

    [s^2 imes m^2=-(sum_{i=1}^{m}v_i)^2+m(v_1^2+...+v_m^2) ]

    所以,我们需要把路程划分为m个部分,使(v_1^2+...+v_m^2)最小。这个可以用动态规划来完成。设(f[i][j])表示将前i个数划分成j段的最小值。我们有如下状态转移方程:

    [f[i][j]=max(f[k][j-1]+(sum[i]-sum[k])^2) ]

    然后这个转移方程可以用斜率优化。

    代码

    #include <iostream>
    #include <cstdio>
    #define int long long
    #define N 3002
    using namespace std;
    int n,m,i,j,v[N],sum[N],f[N][N],q[N],head,tail;
    int read()
    {
    	char c=getchar();
    	int w=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c<='9'&&c>='0'){
    		w=w*10+c-'0';
    		c=getchar();
    	}
    	return w;
    }
    double k(int x,int i,int j)
    {
    	return 1.0*((f[i][x]+sum[i]*sum[i])-(f[j][x]+sum[j]*sum[j]))/(sum[i]-sum[j]);
    }
    signed main()
    {
    	n=read();m=read();
    	for(i=1;i<=n;i++){
    		v[i]=read();
    		sum[i]=sum[i-1]+v[i];
    		f[i][1]=sum[i]*sum[i];
    	}
    	for(j=2;j<=m;j++){
    		head=tail=1;
    		q[1]=j-1;
    		for(i=j;i<=n;i++){
    			while(head<tail&&k(j-1,q[head],q[head+1])<2*sum[i]) head++;
    			int x=q[head];
    			f[i][j]=f[x][j-1]+(sum[i]-sum[x])*(sum[i]-sum[x]);
    			while(head<tail&&k(j-1,q[tail],i)<k(j-1,q[tail],q[tail-1])) tail--;
    			q[++tail]=i;
    		}
    	}
    	printf("%lld
    ",m*f[n][m]-sum[n]*sum[n]);
    	return 0;
    }
    
  • 相关阅读:
    Algorithm Gossip (48) 上三角、下三角、对称矩阵
    .Algorithm Gossip (47) 多维矩阵转一维矩阵
    Algorithm Gossip (46) 稀疏矩阵存储
    Algorithm Gossip (45) 费氏搜寻法
    Algorithm Gossip (44) 插补搜寻法
    Algorithm Gossip (43) 二分搜寻法
    Algorithm Gossip (42) 循序搜寻法(使用卫兵)
    Algorithm Gossip (41) 基数排序法
    Algorithm Gossip (40) 合并排序法
    AlgorithmGossip (39) 快速排序法 ( 三 )
  • 原文地址:https://www.cnblogs.com/LSlzf/p/11877823.html
Copyright © 2011-2022 走看看