HELLO WORLD--一起加油(🍺)!|

kingwzun

园龄:3年6个月粉丝:111关注:0

2022-10-22 11:31阅读: 31评论: 0推荐: 0

Scapegoat Gym - 101775B (贪心+推公式)

题目链接https://vjudge.csgrandeur.cn/problem/Gym-101775B
原文

题意:

现在某人闯祸了,产生了 N 个锅,每个锅有个严重点数,现在可以安排 M 个替罪羊去背锅。

每个替罪羊最多只能背一个锅。若一只羊背一个锅,则该锅的严重点数全部算在它头上;若多只羊背同一个锅,则每个羊分到该锅的一部分的严重点数。

现在考虑一种安排方案,使得所有的身上的严重点数的方差最小。

题解

先考虑上 N 只羊一一对应地背 N 个锅,剩下 M−N 个替罪羊身上严重点数均为 0,当然这样并不是最优解。

应当把再剩下 M−N 个替罪羊安排进 N 个锅里分摊责任,使得方差减小。考虑贪心的思路,每次安排进去一只羊,都要使得方差减小最多。

考虑将新来的羊安排到某个任务,该任务严重点数为 a[i],且原来的背锅羊数是 num,那么首先每个替罪羊分到的严重点数的平均数肯定是不变的
X¯=a[1]+a[2]++a[n]m,因此它原本对方差的贡献为1mnum(a[i]numX¯)2

而现在新加进去一个替罪羊,这个任务对方差的新的贡献为 1m(num+1)(a[i]num+1X¯)2
显然,方差的差值就是Δ=num(a[i]numX¯)2(num+1)(a[i]num+1X¯)2=a[i]2num(num+1)X¯2

因为平均数不变,因此我们让每次挑一个任务让它的a[i]2num(num+1)最大就好了,这个可以用优先队列。

因此代码整体思路就是:

  • 存储替罪羊的数量:num。
  • 将节点放入优先队列,按照a[i]2num(num+1)排序
  • 利用num求出每个锅对方差的贡献,相加

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
const int N=5e5+10;
// const double eps=1e-10;
struct node{
int id, num,a;
double get() const{
return a*a*1.0/num/(num+1);
}
bool operator<(const node &t) const {
return t.get()>this->get();
}
};
signed main()
{
int t;cin>>t;
int Case=1;
while(t--){
priority_queue<node> q;
int n,m;
scanf("%lld %lld",&n,&m);
double aver=0;
for(int i=1;i<=n;i++){
int x;
scanf("%lld",&x);
aver+=x;
q.push({i,1,x});
}
aver/=m;
for(int i=0;i<m-n;i++){
node x=q.top();q.pop();
x.num+=1;
q.push(x);
}
double ans=0;
while(q.size()){
node x=q.top();q.pop();
ans+=x.num*(x.a*1.0/x.num-aver)*(x.a*1.0/x.num-aver);
}
ans/=m;
printf("Case #%lld: %.12lf\n",Case++,ans);
}
return 0;
}

本文作者:kingwzun

本文链接:https://www.cnblogs.com/kingwz/p/16815694.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   kingwzun  阅读(31)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起