Description
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
分析
因为 m 是个常数, S2 是个常数,所以只要最小化
即可
设 f[j][i] 表示前 i 段路,分成 j 天的最优方案对应上式的值,则有
直接以这个式子进行划分DP,状态数为 O(nm) ,时间复杂度为 O(nm2) ,预计得分 60 分。
尝试进行优化。首先,二维的状态存储,显然第一维是可以滚动的,设 g(i)=f[j−1][i] 。考虑 k 的两个取值 k=a 和 k=b ( a>b ),若 a 比 b 优,则有
然后就没了
代码
#include <bits/stdc++.h>
#define sqr(x) (1ll * (x) * (x))
int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
const int N = 3050;
int n,m;
int a[N],s[N];
long long f[N],g[N];
double slope(int a,int b)
{
return (double)(g[a] - g[b] + sqr(s[a]) - sqr(s[b])) / (double)(s[a] - s[b]);
}
int main()
{
int n = read(), m = read();
for (int i = 1; i <= n; i++)
a[i] = read();
for (int i = 1; i <= n; i++)
s[i] = s[i - 1] + a[i];
for (int i = 1; i <= n; i++)
f[i] = INT_MAX;
for (int j = 1; j <= m; j++)
{
memcpy(g, f, sizeof(f));
memset(f, 0, sizeof(f));
static int q[N];
int l = 0, r = -1;
q[++r] = 0;
for (int i = 1; i <= n; i++)
{
while (l < r && slope(q[l + 1], q[l]) < 2 * s[i])
l++;
int t = q[l];
f[i] = g[t] + sqr(s[i] - s[t]);
while (l < r && slope(q[r], q[r - 1]) > slope(q[r], i))
r--;
q[++r] = i;
}
}
printf("%d\n",(int)(f[n] * m - sqr(s[n])));
}