2017EC-Scapegoat-(思维+优先队列贪心)

B

题意:
就是给你n个压力,然后有m个人可以去承担,每个人只能承担一个,现在问你如何去分配这m个人。使得这m个人的压力方差最小。

思考:
刚开始我还以为是压力的方差最小,害,区域赛的题目确实都很难懂。然后题目保证了m>=n,刚开始由于我读错了,所以直接对于每个压力排序后,从后面开始尽量让当前值和va[1]一样,题意错了就不说了。那么既然保证了m>=n,很明显每个压力可以先分配一个人,然后剩下的人怎么分配?,发现对于每个压力对答案的贡献就是(当前的压力-所有人压力的平均值)✖这个压力的人数。由于平均压力是定值。所以用一个单调队列,结构体的排序按,如果这个压力增加一个人,可以让方差更小,那么就给他加一个人,就这样贪心m-n即可。最后遍历对列,把每个压力对答案的贡献算上,最后别忘记了除以m,方差的公式。
值得注意的是,虽然给了3秒,但是队列的取出和放入也是很占时间的,所以这题还卡cin和ll,所以尽量用scanf和int吧。

代码:

int T,n,m,k;
db va[N];
db sum,ans;

struct node{
	int a,b; //那个压力,有多少人
	db c; //平均每个人的压力
	friend operator < (node A,node B)
	{
		db t1 = (A.c-sum)*(A.c-sum)*A.b; //不加人对答案的贡献
		db t2 = (va[A.a]/(A.b+1)-sum)*(va[A.a]/(A.b+1)-sum)*(A.b+1); //加人对答案的贡献
		db t3 = (B.c-sum)*(B.c-sum)*B.b;
		db t4 = (va[B.a]/(B.b+1)-sum)*(va[B.a]/(B.b+1)-sum)*(B.b+1);
		db x1 = t1-t2,x2 = t3-t4; //尽量让差大的去加人
		return x1<x2;
	}
};

signed main()
{
	IOS;
	scanf("%d",&T);
	priority_queue<node> q;
	for(int cs=1;cs<=T;cs++)
	{
		sum = ans = 0;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++) scanf("%lf",&va[i]);
		for(int i=1;i<=n;i++) sum += va[i];
		sum /= m; //m个人的压力平均值
		for(int i=1;i<=n;i++) q.push({i,1,va[i]});
		int last = m-n;
		while(last--)
		{
			auto now = q.top();q.pop();
			q.push({now.a,now.b+1,va[now.a]/(now.b+1)}); //让最优的去加人
		}
		while(q.size())
		{
			auto now = q.top();q.pop();
			ans += (now.c-sum)*(now.c-sum)*now.b; //贡献*人数
		}
		ans /= m;
		printf("Case #%d: %.8lf\n",cs,ans);
	}
	return 0;
}

总结:
多多积累经验呀,多多思考,队列是个贪心的好方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值