笔试题4 -- 除2!(k次机会偶数除2求最小和)

除2!(k次机会偶数除2求最小和)


题目链接: 除2! (nowcoder.com)

题目描述

给一个数组,一共有 n 个数。你能进行最多 k 次操作。

每次操作可以进行以下步骤:

  • 选择数组中的一个偶数 ai,将其变成 ai/2 。

现在你进行不超过 k 次操作后,让数组中所有数之和尽可能小。请输出这个最小的和。

输入描述

  • 第一行输入两个正整数 n 和 k ,用空格隔开

  • 第二行输入 n 个正整数 ai

数据范围

1≤n≤100000,1≤k≤109,1≤ai≤109

输出描述

一个正整数,代表和的最小值。

读懂题目

注意点:

可以用于缩小最后数组中所有数相加和的唯一方法是:

  • 将数组中某个偶数减半
  • 而减半次数最多有k次

我们需要知道这个减半操作有两个限制项:

  1. 偶数
  2. 最多执行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 较大时。通过优先处理最大的偶数,我们能够显著减少所需的操作次数,从而在给定的时间限制内找到最小的和。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

螺蛳粉只吃炸蛋的走风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值