中二羊专题:栋栋吃糖果

U163898

题目

题目背景

栋栋参加比赛拿下了一等奖,老师奖励了很多糖果。

题目描述

一共有 m m m 种糖果,其中第i种糖果的数量为 m i m_i mi 。栋栋吃糖时会获得快乐值,并且他喜欢换着口味吃糖。当栋栋吃下第一个糖果时快乐值为 0 0 0 ,接下来,每吃一个不同口味的糖果(与上一个糖不同),快乐值就会增加 5 5 5 点,而连续吃下 k k k 个相同口味的糖果,快乐值就会减少 3 ∗ ( k − 1 ) 3*(k-1) 3(k1) 点。栋栋已经下定决心要吃完所有的糖果。现在他想知道如何安排吃糖的顺序才能使快乐值最大。请你求出最大快乐值。

输入格式

输入分两行
第一行输入整数 m m m
第二行输入 m m m 个整数,分别表示每种糖果的数量 m i m_i mi

输出格式

输出栋栋能获得的最大快乐值

输入输出样例

样例1
输入1
3
3 1 1
输出1
20
样例2
输入2
3
4 1 1
输出2
17

说明/提示

对于 100 % 100\% 100%的数据,有 1 < m ≤ 1000 1<m\le1000 1<m1000 1 ≤ m i ≤ 10000 1\le m_i≤10000 1mi10000 1 < m ≤ 1000 1<m\le1000 1<m1000 1 ≤ m i ≤ 10000 1≤m_i≤10000 1mi10000。保证结果在int范围内。

题解

定义

此题是广义鸽巢原理的一道例题
先说一下什么是鸽巢原理:
定义:如果 k + 1 k+1 k+1 个或更多个物体放入 k k k 个盒子,那么至少有一个盒子包含了 2 2 2 个或更多的物体。此定理也被称为狄利克雷抽屉原理。
但是此题用到的是广义鸽巢原理。
定义:如果 N N N 个物体放入 k k k 个盒子,那么至少有一个盒子包含了至少 ⌈ N k ⌉ \left\lceil\dfrac{N}{k}\right\rceil kN 个物体。
举个例子,在100人中,至少有 ⌈ 100 12 ⌉ = 9 \left\lceil\dfrac{100}{12}\right\rceil=9 12100=9 个人的生日在同一个月。


看完题后,我们会发现,有许多东西我们都不需要。我们现在需要的是 N N N k k k
用总糖果数量减去最大数量糖果类型可以得出其它糖果数量综合。
所以处理情况是这样的:

...
signed main(){
	int m,md=0,ans=0,sum,a;
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		scanf("%d",&a);
		ans+=a;
		md=max(md,a);
	}
	ans-=md;
	...
	return 0;
}

当我们发现吃糖时不用减少快乐值时,即吃重复糖最多只吃一次时:

...
	if(ans-md>=-1){
		printf("%d",(ans+md-1)*5);
		return 0;
	}
...

当我们发现必须减少快乐值时:
如果不计损失的快乐值,那么就只有 a n s ∗ 10 ans*10 ans10 的快乐值了。
我们可以这么理解:
我们已经知道了其它类糖果总数量为 a n s ans ans ,最大数量糖果类总数量为 m d md md ,所以我们把可以看作有 m d − 1 md-1 md1 个盒子要放下 a n s ans ans 个糖果。(关键思路)
所以代码就出来了,如果求总和会要用到等差数列公式: n ( a + n ) 2 \frac{n(a+n)}{2} 2n(a+n) 。(注意此处等差数列公式是 1 + 2 + . . . + n − 1 + n 1+2+...+n-1+n 1+2+...+n1+n

...
	const int P1=ceil(md/(double)(ans+1));
	const int P2=md/(ans+1);
	const int P3=md%(ans+1);
	sum-=3*((ans+1-P3)*(P2-1)*P2/2+P3*P1*(P1-1)/2);
	printf("%d",sum);
...

代码(不建议看)

#include<iostream>
#include<cstdlib>
#include<cmath>
#include<cstdio>
using std::max;
signed main(){
	int m,md(0),ans(0),sum,a;
	scanf("%d",&m);
	for(register int i(1);i<=m;i=-~i){scanf("%d",&a);md=max(md,a),ans+=a;}
	ans-=md;
	if(ans-md>=-1){
		printf("%d",(ans+md-1)*5);
		exit(0);
	}
	sum=ans*10;
	const int P1=ceil(md/(double)(ans+1));
	const int P2=md/(ans+1);
	const int P3=md%(ans+1);
	sum-=3*((ans+1-P3)*(P2-1)*P2/2+P3*P1*(P1-1)/2);
	printf("%d",sum);
	exit(0);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值