题目
Pine开始了从S地到T地的征途。
从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站。
Pine计划用m天到达T地。除第m天外,每一天晚上Pine都必须在休息站过夜。所以,一段路必须在同一天中走完。
Pine希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小。
帮助Pine求出最小方差是多少。
设方差是v,可以证明,v×m^ 2是一个整数。为了避免精度误差,输出结果时输出v×m^ 2。
Input
第一行两个数 n、m。
第二行 n 个数,表示 n 段路的长度
Output
一个数,最小方差乘以 m^2 后的值
Sample Input
5 2
1 2 5 8 6
Sample Output
36
Hint
1≤n≤3000,保证从 S 到 T 的总路程不超过 30000
思路:这道题也是典型的斜率优化DP,不过自己之前没有好好看网上大神的代码,仅仅看了学姐给的代码,后来发现,学姐给的代码我估摸着只有她那种经常拿金牌的大佬才能看懂,思路不一样,以至于在网上学的时候很迷惑,这篇博客算是将自己带入"正轨",用平常的方法来做吧~
关于状态转移以及选取边界的问题可以看这位大佬的斜率优化DP
然后再看看这个落谷第二个解法,相信会有所启发的,鄙人不才,没有能力讲好~
个人注意点:
1.明白最后弄出来的一次函数,谁是变量,谁是因变量
2.明白枚举的是什么~
代码如下:
#include<cstring>
#include<cstdio>
#include<queue>
#define N 3005
#define int long long
int f[N][N];//纪录结果
int que[N];//记录队列
int sl[N];//纪录前缀和
int l[N];//纪律输入的
int n,m;
int min(int a,int b){return a<b?a:b;}
double slope(int u,int j,int k){return double(f[u][j]-f[u][k]+sl[j]*sl[j]-sl[k]*sl[k])/(double)(sl[j]-sl[k]);}
//这个嘛,就是算斜率,关键之处~
main(){
scanf("%lld%lld",&n,&m);
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;++i)scanf("%lld",&l[i]);
for(int i=1;i<=n;++i)sl[i]=sl[i-1]+l[i];
int h,t;
f[0][0]=0;
for(int i=1;i<=n;i++)f[1][i]=sl[i]*sl[i];
for(int p=2;p<=m;p++){//我没有滚动数组,只好用二维数组,枚举n^2
h=1,t=0;//分别枚举的是f[i][j]中的i和j
for(int i=1;i<=n;i++) {
while(h<t&&slope(p-1,que[h],que[h+1])<2*sl[i])h++;
int j=que[h];
f[p][i]=f[p-1][j]+(sl[j]-sl[i])*(sl[j]-sl[i]);
while(h<t&&slope(p-1,que[t-1],que[t])>slope(p-1,que[t],i))t--;
que[++t]=i;
}
}
int ans=m*f[m][n]-sl[n]*sl[n];
printf("%lld\n",ans);
return 0;
}