• 【ybt金牌导航1-3-3】【luogu P4072】征途


    征途

    题目链接:ybt金牌导航1-3-3 / luogu P4072

    题目大意

    有 n 个值,你要把它划分成 m 段,使得把每段的值加起来得到段的值,段值的方差最小。
    输出这个最小方差乘 m^2,可证明这个是整数。

    思路

    .首先我们考虑化简式子:(设分段之后每一段的长度是 (d_i)(overline{x}) 是它们的平均值,即 (overline{x}=frac{sum_{i=1}^{m}d_i}{m})
    (s^2 imes m^2)
    (=dfrac{sumlimits_{i=1}^{m}(d_i-overline{x})^2}{m} imes m^2)
    (=sumlimits_{i=1}^{m}(d_i-overline{x})^2 imes m)
    (=sumlimits_{i=1}^{m}(d_i^2-2 imes d_i imes overline{x}+overline{x}^2) imes m)
    (=m imessumlimits_{i=1}^{m}{d_i^2}+sumlimits_{i=1}^{m}(-2 imes d_i imes frac{sum_{i=1}^{m}d_i}{m}+(frac{sum_{i=1}^{m}d_i}{m})^2) imes m)
    (=m imessumlimits_{i=1}^{m}{d_i^2}+sumlimits_{i=1}^{m}(-2 imes d_i imes sum_{i=1}^{m}d_i+(frac{sum_{i=1}^{m}d_i}{m})(sum_{i=1}^{m}d_i)))

    我们再弄一个 (sum=sum_{i=1}^{m}d_i)
    (=m imessumlimits_{i=1}^{m}{d_i^2}+sumlimits_{i=1}^{m}(-2 imes d_i imes sum+(frac{sum}{m}) imes sum))
    (=m imessumlimits_{i=1}^{m}{d_i^2}+(-2) imes sumlimits_{i=1}^{m}d_i imes sum+sumlimits_{i=1}^{m}(frac{1}{m}) imes sum^2)
    (=m imessumlimits_{i=1}^{m}{d_i^2}+(-2) imes sum imes sum+sum^2)
    (=m imessumlimits_{i=1}^{m}{d_i^2}-2 imes sum^2+sum^2)
    (=m imessumlimits_{i=1}^{m}{d_i^2}-sum^2)

    那你会发现,你就是要让 (sumlimits_{i=1}^{m}d_i^2) 最小。

    那你可以设 (f_{i,j}) 为前 (i) 个数划分成了 (j) 段的最小值。
    那会有这个方程:(f_{i,j}=minlimits_{k=1}^{i-1}{f_{k,j-1}+(s_i-s_k)^2})

    那可以看出它是有单调性的,那我们就直接上分治,做就好了。

    代码

    #include<cstdio>
    #include<iostream>
    #define ll long long
    
    using namespace std;
    
    int n, m;
    ll a[3001], s[3001], f[3001][3001];
    
    void slove(int op, int l, int r, int L, int R) {//分治
    	int mid = (l + r) >> 1, pl = 0;
    	ll minn = 1e15;
    	for (int i = L; i <= R && i < mid; i++) {//对于处理范围中间的点用枚举答案范围求答案
    		ll now = f[i][op - 1] + (s[mid] - s[i]) * (s[mid] - s[i]);
    		if (now < minn) {
    			pl = i;
    			minn = now;
    		}
    	}
    	
    	f[mid][op] = minn;
    	
    	if (l < mid) slove(op, l, mid - 1, L, pl);
    	if (mid < r) slove(op, mid + 1, r, pl, R);
    }
    
    int main() {
    	scanf("%d %d", &n, &m);
    	for (int i = 1; i <= n; i++) {
    		scanf("%lld", &a[i]);
    		s[i] = s[i - 1] + a[i];
    	}
    	
    	for (int i = 1; i <= n; i++)//预处理只有一段的
    		f[i][1] = s[i] * s[i];
    	for (int i = 2; i <= m; i++)
    		slove(i, 1, n, 1, n);
    	
    	printf("%lld", f[n][m] * m - s[n] * s[n]);//记得套回公式里
    	
    	return 0;
    } 
    
  • 相关阅读:
    数据结构-连续子数组的最大和
    数据结构-并查集
    nginx开启代理后,出现net::ERR_CONTENT_LENGTH_MISMATCH 200 (OK)错误
    计算出n阶乘中尾部零的个数
    常见的加密方式总结
    idea中lombok不生效
    纯真ip地址数据库使用
    go mod使用
    exec: "gcc": executable file not found in %PATH%解决办法
    java多线程系列1-初识多线程多线程4种实现方式
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_1-3-3.html
Copyright © 2020-2023  润新知