贪心(优先队列)-Minimize the error - CodeForces - 960B

贪心(优先队列)-Minimize the error - CodeForces - 960B

题意:

给 定 两 个 长 度 为 n 的 数 组 a 和 b , 对 a 操 作 k 1 次 , 对 b 操 作 k 2 次 , 每 次 操 作 可 以 选 择 数 组 中 的 一 个 数 , 使 其 增 大 或 减 小 1 。 求 经 过 一 系 列 操 作 后 , ∑ i = 1 n ( a i − b i ) 2 的 最 小 值 。 给定两个长度为n的数组a和b,对a操作k_1次,对b操作k_2次,每次操作可以选择数组中的一个数,使其增大或减小1。\\求经过一系列操作后,\sum_{i=1}^{n}(a_i-b_i)^2的最小值。 nabak1bk2使1i=1n(aibi)2

Input:
2 0 0
1 2
2 3

Output:
2


Input:
2 1 0
1 2
2 2

Output:
0


Input:
2 5 7
3 4
14 4

Output:
1

数据范围:

n ∈ [ 1 , 1000 ] , k 1 + k 2 ∈ [ 0 , 1000 ] , a i , b i ∈ [ − 1 0 6 , 1 0 6 ] 。 T i m e   l i m i t : 1000 m s , M e m o r y   l i m i t : 262144 k B 。 n∈[1,1000],k1+k2∈[0,1000],a_i,b_i∈[-10^6,10^6]。\\Time \ limit:1000 ms,Memory\ limit:262144 kB。 n[1,1000]k1+k2[0,1000]ai,bi[106,106]Time limit1000msMemory limit262144kB

题解:

对 表 达 式 ∑ i = 1 n x i 2 > = ( ∑ i = 1 n x i ) 2 n , 当 且 仅 当 x 1 = x 2 = . . . = x n 时 取 等 , 由 均 值 不 等 式 , 可 知 , 最 小 值 在 n 个 数 相 等 时 取 到 , 要 使 表 达 式 的 值 尽 量 小 , n 个 项 大 小 应 当 尽 量 接 近 。   对 本 题 而 言 , 考 察 d i = ∣ a i − b i ∣ , 每 次 操 作 可 以 使 得 d i 增 大 或 减 小 1 , 总 操 作 数 为 k = k 1 + k 2 。   因 此 , 最 优 解 应 当 从 数 组 d 的 最 大 值 开 始 , 从 大 到 小 开 始 进 行 操 作 , 每 次 减 小 最 大 的 d m a x , 直 到 k 次 操 作 完 。 对表达式\sum_{i=1}^{n}x_i^2>=\frac{(\sum_{i=1}^{n}x_i)^2}{n},当且仅当x_1=x_2=...=x_n时取等,\\由均值不等式,可知,最小值在n个数相等时取到,要使表达式的值尽量小,n个项大小应当尽量接近。\\\ \\对本题而言,考察d_i=|a_i-b_i|,每次操作可以使得d_i增大或减小1,总操作数为k=k_1+k_2。\\ \ \\因此,最优解应当从数组d的最大值开始,从大到小开始进行操作,每次减小最大的d_{max},直到k次操作完。 i=1nxi2>=n(i=1nxi)2x1=x2=...=xnn使n di=aibi使di1k=k1+k2 ddmaxk

需要注意的是:

① 、 一 开 始 这 样 想 的 , 给 数 组 d 排 序 , 接 着 从 大 到 小 , 每 次 给 最 大 的 d 尽 量 的 减 小 到 0 , 即 : 若 剩 余 的 操 作 次 数 k > = d i , 就 把 d i 置 0 , 剩 余 k − d i 次 操 作 ; 若 k < d i , 就 把 d i 减 小 k 。 后 来 发 现 其 实 数 组 d 中 的 值 应 当 是 动 态 变 化 的 , 在 最 大 的 d m a x 逐 渐 减 小 的 过 程 中 , d m a x 可 能 会 减 小 到 次 小 项 , 原 来 的 次 小 项 会 成 为 d m a x 。 所 以 , 要 逐 渐 减 小 最 大 的 d , 需 要 用 到 优 先 队 列 来 解 决 这 个 问 题 。   ② 、 在 计 算 d 平 方 时 , 极 端 情 况 下 会 爆 i n t , 所 以 为 了 方 便 起 见 , d 数 组 定 义 为 l l 类 型 。 ①、一开始这样想的,给数组d排序,接着从大到小,每次给最大的d尽量的减小到0,即:\\若剩余的操作次数k>=d_i,就把d_i置0,剩余k-d_i次操作;若k<d_i,就把d_i减小k。\\后来发现其实数组d中的值应当是动态变化的,在最大的d_{max}逐渐减小的过程中,d_{max}可能会减小到次小项,\\原来的次小项会成为d_{max}。所以,要逐渐减小最大的d,需要用到优先队列来解决这个问题。\\\ \\②、在计算d平方时,极端情况下会爆int,所以为了方便起见,d数组定义为ll类型。 dd0k>=didi0kdik<didikddmaxdmaxdmaxd dint便dll

具体落实:

① 、 计 算 数 组 d , 将 每 一 个 d [ i ] 存 储 到 优 先 队 列 中 。 ② 、 “ 一 次 一 次 ” 的 操 作 , 取 最 大 的 d m a x 出 队 , 将 其 减 小 1 , 再 将 其 入 队 , 重 复 操 作 直 到 k = 0 。 ③ 、 将 队 列 中 的 所 有 元 素 出 队 , 计 算 最 终 的 平 方 和 。 ①、计算数组d,将每一个d[i]存储到优先队列中。\\②、“一次一次”的操作,取最大的d_{max}出队,将其减小1,再将其入队,重复操作直到k=0。\\③、将队列中的所有元素出队,计算最终的平方和。 dd[i]dmax1k=0


代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#define inf 0x7fffffff
#define ll long long
using namespace std;
const int N=1010;
int n,k1,k2;
int a[N],b[N];
ll d[N];
priority_queue<int> Q;

int main()
{
    scanf("%d%d%d",&n,&k1,&k2);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++) {scanf("%d",&b[i]);d[i]=abs(b[i]-a[i]);Q.push(d[i]);}

    int k=k1+k2;
    while(k>0)
    {
        int tmp=Q.top();
        Q.pop();
        Q.push(abs(tmp-1));
        k--;
    }

    ll ans=0;
    while(!Q.empty())
    {
        ll tmp=Q.top();
        ans+=(tmp*tmp);
        Q.pop();
    }

    printf("%lld\n",ans);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值