链接:https://ac.nowcoder.com/acm/contest/8563/A
题目
给一个数组,一共有 n个数。
你能进行最多 k次操作。每次操作可以进行以下步骤:
选择数组中任意的一个偶数 a_i,将其变成 a_i/2 。
现在你进行不超过k次操作后,让数组中所有数之和尽可能小。请输出这个最小的和。
思路
真的没想到自己没爆0,一开始只是抱着长见识的心情参加的,想不到出题人如此仁慈,跪谢大佬!
题意非常清楚,给你n个数字,你可以对其中任意一个偶数进行取半操作。
在不超过k次操作的情况下,问你能得到的所有数的最小和。
首先我们先考虑,如果一个偶数没有,我们就直接输出序列和就ok了。而且我们只需要对偶数操作,奇数就不需要存储起来。
所以对于输入的数字,奇数就给它求和,偶数存储起来。
对于偶数的处理,我相信基本上大家应该都能想到,我们每次都去对序列中最大的偶数取半就可以了。
(应该不会有想不出来的吧,不会吧,不会吧~)
那这样就产生了一个难题:怎么在不超时的前提下去维持偶数序列的有序呢?
我们肯定不能每次都排个序然后去找最大值,会T掉的。(我写了,一发T了,有什么好说的?)
这时我的脑海里出现了一个东西:优先队列(priority_queue),这不正是我朝思暮想的东西吗?
所以存储问题就解决了,剩下的就easy了。
还有一点就是偶数除2还可能会变成奇数,这个时候我们就不用处理它了,把他移除队列就好。
剩下的看代码,伊丽莎白!
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll; //int会爆掉,开long long
int main()
{
ll n,m,k,x;
ll sum=0;
priority_queue<ll>a; //优先队列
scanf("%lld%lld",&n,&k);
for(ll i=0; i<n; i++)
{
scanf("%lld",&x);
if(x%2) //如果是奇数,直接求和
sum+=x;
else //如果是偶数,扔进优先队列里
a.push(x);
}
if(a.empty()) //队列为空,说明全是奇数
printf("%lld\n",sum);
else
{
ll m;
while(k--&&!a.empty()) //既要保证还有操作次数,还要保证还有偶数可以让我们操作
{
m=a.top(); //这里定义的优先队列默认是大顶堆(从大到小)
a.pop(); //把最大的踢出队伍
m/=2;
if(m%2) //如果取半之后是奇数,就算入sum里,不再放回队伍
sum+=m;
else
a.push(m); //还是偶数就再送回队伍
}
while(!a.empty()) //看看还有没有偶数
{
m=a.top();
a.pop();
sum+=m;
}
printf("%lld\n",sum);
}
}
开心开心~~
蓝桥国赛冲冲冲!!!