Day3 T3 跳跃机

Day3 T3 跳跃机

移步原题

题目大意:

给定 n 个数,将它们分为 m 组,使得这 m 组数字之和的方差尽可能的小,设这个最小方差为 v ,请输出 v*m^2

数据范围:对于 100% 的数据,1≤n≤3000 且保证从起点到终点的总长度不超过 30000

原始做法:

因为注意到 n 并不是很大,故考虑对每组选取的数进行DP

我们先来推一波柿子

设这 m 组数的和分别为 a_1,a_2...a_m 他们的的平均数为 x

可列: $v=\frac{(x-a_1)^2+(x-a_2)^2+…+(x-a_m)^2}{m}$,

x=\frac{a_1+a_2+...+a_3}{m} 代入得:

$v=\frac{\frac{\sum\limits_{i=1}^{m}a_i}{m}-a_1)^2+(\frac{\sum\limits_{i=1}^{m}a_i}{m}-a_2)^2+…+(\frac{\sum\limits_{i=1}^{m}a_i}{m}-a_m)^2}{m}$,

化简得:
v=\frac{{a_1}^2+{a_2}^2+...+{a_m}^2}{m}- \frac{(\sum\limits_{i=1}^{m}a_i)^2}{m^2}

最后乘上 m^2
v*m^2 = m X ({a_1}^2+{a_2}^2+...+{a_m}^2)- (\sum\limits_{i=1}^{m}a_i)^2

接下来就是愉快的推DP方程

f_{i,k} 表示将前 i 个数分为 k 组得到的最小的 和的平方之和(有点绕,即上述式中的 a_1^2+a_2^2+...+a_m^2 ) 再用一个 sum 数组预处理前 i 个数的和 接着枚举第 k-1 组的最后一个数,记为第 j 个数

能轻松得到方程式:
f_{i,k}=min(f_{i,k},f_{j,k-1}+(sum_i-sum_j)^2)

最终答案就是:
f_{n,m} X m - {sum_n}^2

复杂度为 O(n^2m) ,自信一发,哼哼……

90分……

注意到这坑比的数据范围,可没说 m 的大小啊 (ノ`Д)ノ 最大的那个点直接 TLE ┭┮﹏┭┮

正解

考虑斜率优化,复杂度可降为 O(nm)

那么问题来了?什么是斜率优化?

接下来 我也不会讲的

不了解斜率优化的同学可以看看这篇博客学习一下,个人感觉这位博主解释的很清楚了blog by _ducati

现在,我们需要将我们的DP方程改为斜率式 y=kx+b

f_{i,k}=f_{j,k-1}+(sum_i-sum_j)^2

展开可得:$f_{i,k}=f_{j,k-1}+{sum_i}^2+{sum_j}^2-2$ X sum_i X sum_j ,

移项得:$f_{j,k-1}+{sum_i}^2+{sum_j}^2=2$ X sum_i X sum_j+f_{i,k}
&nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp y&nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp = &nbsp k&nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp &nbsp x&nbsp &nbsp &nbsp &nbsp &nbsp +&nbsp &nbsp &nbsp &nbsp b

接下来就是直接套斜率优化的模板了

什么?想要代码??请先关注天依谢谢喵

小结

其实能推出第一个DP方程就已经不错了,
斜率式的话靠感觉,把原方程展开,移移项也是能推出来的, 可能是玄学了点
得出斜率式就可以套模板了 还挺方便
私货

最后上代码

#include <bits/stdc++.h>
using namespace std;

int n,m;
long long sum[3005],a[3005],q[3005],dp[3005][3005];

double x(long long a,long long b)
{
	return dp[a][b-1]+sum[a]*sum[a];
}
double y(long long u,long long v,long long t)
{
	return 1.000000*(x(u,t)-x(v,t))/(sum[u]-sum[v]);
}

int main()
{
	
	cin >> n >> m;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
	
	for(int i=1;i<=n-m+1;i++) dp[i][1]=sum[i]*sum[i];
	
	for(int i=2;i<=m;i++)
	{
		int s=0,e=0;
		for(int j=1;j<=n;j++)
		{
			while(s<e and 2*sum[j]>y(q[s],q[s+1],i)) s++;
			long long p=q[s];
			dp[j][i]=dp[p][i-1]+(sum[j]-sum[p])*(sum[j]-sum[p]);
			while(e>s and y(q[e-1],q[e],i)>y(q[e-1],j,i)) e--;
			q[++e]=j;
		}
	}
	
	cout << dp[n][m]*m-sum[n]*sum[n];
	
	
	return 0;
	
}
4 个赞

好贴无人

1 个赞