[周末训练]征途

Posted machinerycountry

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[周末训练]征途相关的知识,希望对你有一定的参考价值。

题目

【内存限制:$256MiB$】【时间限制:$1000ms$】

【标准输入输出】【题目类型:传统】评测方式:文本比较】


 

【题目描述】

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

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

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

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

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

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

【输入格式】

第一行两个数$N,M$

第二行$N$个数,表示$N$段路的长度。

【输出格式】

一个数,最小方差乘以$M^2$后的值。

【样例】

样例输入

5 2
1 2 5 8 6

样例输出

36

【数据范围与提示】

对于$30\\%$的数据,$1 leq N leq 10$
对于$60\\%$的数据,$1 leq N leq 100$
对于$100\\%$的数据,$1 leq N leq 3000$
保证从$S$到$T$的总路程不超过$30000$

题解

【做题经历】

找到了本题关键之后,打了个裸的$DP$,结果卡在初始化上面,后来发现这个裸$DP$可得$80pts$

【正解】

看了看题目,发现题目要我们求的东西似乎没怎么给明,那么我们首先应该手动化简或者说是处理一下题目给出的公式$v*M^2$。

首先,我们设题目中的$N$个数$a_1,a_2......a_N$通过某种方式的组合后变为$M$个数$x_1,x_2......x_M$。

令$S=sum_{i=1}^{M}x_i$

那么通过方差公式可得

$$v=frac{sum_{i=1}^{M}(x_i-frac{S}{M})}{M}$$

通过平方差公式展开,可得

$$v=frac{sum_{i=1}^{M}({x_i}^2+frac{S^2}{M^2}-2x_ifrac{S}{M})}{M}$$

而我们要求的是$v×M^2$,不要忘记$M^2$,现在将$M^2$乘进去,可得

$$v*M^2=Mcdot sum_{i=1}^{M}({x_i}^2+frac{S^2}{M^2}-2x_ifrac{S}{M})$$

将括号打开,可得

$$vM^2=Msum_{i=1}^{M}{x_i}^2+M^2frac{S^2}{M^2}-2Mfrac{S}{M}sum_{i=1}^{M}x_i$$

因为$S=sum_{i=1}^{M}x_i$,所以我们还可以将这个式子化简,可得

$$vM^2=Msum_{i=1}^{M}{x_i}^2+S^2-2S^2$$

最后,可以得到

$$v×M^2=Msum_{i=1}^{M}{x_i}^2-S^2$$

在这个式子中,$M$与$S$都是已知的,所以我们只需使求和部分最大即可。

然后开始$DP$:令

$dp[j][i]$:前$i$个数$a_i$组合成$j$个数$x_j$后平方求和$x_j$的最大值。

$s[i]$:前缀和不解释。

那么就有状转$$dp[j][i]=max{dp[j-1][k]+(s[i]-s[k])^2|kin [0,i-1]}$$

然后我们可以码出$80pts$的代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
template<class T>inline void qread(T& x){
    char c;bool f=false;x=0;
    while((c=getchar())<0||9<c)if(c==-)f=true;
    for(x=(c^48);0<=(c=getchar())&&c<=9;x=(x<<1)+(x<<3)+(c^48));
    if(f)x=-x; 
}
template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
inline int rqread(){
    char c;bool f=false;int x=0;
    while((c=getchar())<0||9<c)if(c==-)f=true;
    for(x=(c^48);0<=(c=getchar())&&c<=9;x=(x<<1)+(x<<3)+(c^48));
    return f?-x:x;
}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
const int MAXN=3000;
int N,M,a[MAXN+5],s[MAXN+5],dp[MAXN+5][MAXN+5];
signed main(){
    qread(N,M);
    for(int i=1;i<=N;++i)s[i]=s[i-1]+(a[i]=rqread());
    memset(dp,0x3f,sizeof dp);dp[0][0]=0;
    for(int j=1;j<=M;++j)for(int i=1;i<=N;++i)for(int k=0;k<i;++k)
        dp[j][i]=Min(dp[j-1][k]+(s[i]-s[k])*(s[i]-s[k]),dp[j][i]);
    printf("%lld
",M*dp[M][N]-s[N]*s[N]);
    return 0;
}

这是一个裸的$DP$,可以发现其时间复杂度为$O(NM^2)$,对于最大的$N$为$3000$时,还是会超时的。

考虑为其优化,再看其形式,发现其形如$f(x)=max{g(x)}+tmp$,那么这个时候我们就要斜率优化一下。

然后,可以硬扯下一维,最终时间复杂度为$O(NM)$,代码如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
template<class T>inline void qread(T& x){
    char c;bool f=false;x=0;
    while((c=getchar())<0||9<c)if(c==-)f=true;
    for(x=(c^48);0<=(c=getchar())&&c<=9;x=(x<<1)+(x<<3)+(c^48));
    if(f)x=-x; 
}
template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
inline int rqread(){
    char c;bool f=false;int x=0;
    while((c=getchar())<0||9<c)if(c==-)f=true;
    for(x=(c^48);0<=(c=getchar())&&c<=9;x=(x<<1)+(x<<3)+(c^48));
    return f?-x:x;
}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
const int MAXN=3000;
const int BUFFER_SIZE=3000;
int N,M,a[MAXN+5],s[MAXN+5],dp[MAXN+5][MAXN+5];
int Q[BUFFER_SIZE+5],head,tail;
inline void init(){head=1,tail=0;}
inline double calc(const int j,const int k,const int l){return (dp[j-1][k]-dp[j-1][l]+s[k]*s[k]-s[l]*s[l])*1.0/(s[k]-s[l]);}
inline int getDp(const int j,const int i,const int k){return dp[j-1][k]+(s[i]-s[k])*(s[i]-s[k]);}
signed main(){
    qread(N,M);
    memset(dp,0x3f,sizeof dp);dp[0][0]=0;
    for(int i=1;i<=N;++i)s[i]=s[i-1]+(a[i]=rqread()),dp[1][i]=s[i]*s[i];
    for(int j=2;j<=M;++j){
        init();
        for(int i=1;i<=N;++i){
            while(head<tail&&calc(j,Q[head],Q[head+1])<2.0*s[i])++head;
            dp[j][i]=getDp(j,i,Q[head]);
            while(head<tail&&calc(j,Q[tail-1],Q[tail])>calc(j,Q[tail],i))--tail;
            Q[++tail]=i;
        }
    }
    printf("%lld
",M*dp[M][N]-s[N]*s[N]);
    return 0;
}

 

以上是关于[周末训练]征途的主要内容,如果未能解决你的问题,请参考以下文章

妙味周末训练营 Angular - 前端最陡峭的框架学习

周末训练 ——购物车&BMI指数系统

题解SDOI2016征途

10行代码集2000张美女图,Python爬虫120例,再上征途

10行代码集2000张美女图,Python爬虫120例,再上征途

Python征途-01.Python概述