除2!(k次机会偶数除2求最小和)
文章目录
- 除2!(k次机会偶数除2求最小和)
- 读懂题目
- 方案一(基于multiset实现 -- 超时)
- 方案二(改进算法--基于 priority_queue 实现)
- 总结
题目链接: 除2! (nowcoder.com)
题目描述
给一个数组,一共有 n 个数。你能进行最多 k 次操作。
每次操作可以进行以下步骤:
- 选择数组中的一个偶数 ai,将其变成 ai/2 。
现在你进行不超过 k 次操作后,让数组中所有数之和尽可能小。请输出这个最小的和。
输入描述
第一行输入两个正整数 n 和 k ,用空格隔开
第二行输入 n 个正整数 ai
数据范围
1≤n≤100000,1≤k≤109,1≤ai≤109
输出描述
一个正整数,代表和的最小值。
读懂题目
注意点:
可以用于缩小最后数组中所有数相加和的唯一方法是:
- 将数组中某个偶数减半
- 而减半次数最多有k次
我们需要知道这个减半操作有两个限制项:
- 偶数
- 最多执行k次
而如果减半次数没有用完,数组中已经没有偶数了,那么减半操作也只能停止。
方案一(基于multiset实现 – 超时)
//使用 multiset:50%案例超时
int main()
{
int n = 0, k = 0;
cin >> n >> k;
vector<int> v(n, 0);
for(int i = 0; i < n; i++)
{
cin >> v[i];
}
multiset<int, greater<>> s(v.begin(), v.end());
bool have_double_num = false;
for(int i = 0; i < k; i++)
{
int big_num = 0;
for(auto it = s.begin(); it != s.end(); it++)
{
if(*it % 2 == 0)
{
big_num = *it;
have_double_num = true;
s.erase(it);
break;
}
}
big_num /= 2;
s.insert(big_num);
if(!have_double_num) { break; }
}
size_t sum = 0;
for(auto num: s)
{
sum += num;
}
cout << sum << endl;
return 0;
}
提交截图:
复杂度分析:
- 时间复杂度:由于
multiset
的每次插入和删除操作都是 O(log n),而且在最坏情况下,可能需要遍历整个multiset
来找到偶数进行操作,因此总的时间复杂度是 O(n log n)。- 空间复杂度:
multiset
存储了所有的元素,因此空间复杂度是 O(n)。
方案二(改进算法–基于 priority_queue 实现)
代码示例:
// 使用 priority_queue: 通过全部用例
int main() {
int n = 0, k = 0;
cin >> n >> k;
vector<int> v(n, 0);
for(int i = 0; i < n; i++) {
cin >> v[i];
}
// 使用优先队列来存储元素,最大的元素总是在队列的前面
size_t sum = 0;
priority_queue<pair<int, bool>> pq;
for (int num : v) {
if(num % 2 == 0)
pq.push({num, num % 2 == 0});
else
{
sum += num;
}
}
while(k > 0 && !pq.empty()) {
auto [big_num, is_even] = pq.top(); pq.pop();
// 只有当最大的数是偶数时,我们才执行除以2的操作
if(is_even) {
pq.push({big_num / 2, (big_num / 2) % 2 == 0});
k--;
}
else
{
sum += big_num;
}
}
while(!pq.empty()) {
sum += pq.top().first; pq.pop();
}
cout << sum << endl;
return 0;
}
提交截图:
复杂度分析:
- 时间复杂度:
priority_queue
的插入和删除最大元素的操作是 O(log n),并且由于您只处理最大的偶数,所以不需要遍历整个队列,总的时间复杂度是 O(k log n)。- 空间复杂度:与
multiset
类似,priority_queue
也存储了所有的元素,因此空间复杂度是 O(n)。
总结
在解决这类优化问题时,选择合适的数据结构至关重要。虽然 multiset
提供了一个简单直观的解决方案,但它在处理大量数据时可能会导致超时。相比之下,priority_queue
提供了一个更高效的方法,特别是当操作次数 k 较大时。通过优先处理最大的偶数,我们能够显著减少所需的操作次数,从而在给定的时间限制内找到最小的和。