Set or Decrease(贪心法)

题目链接

题目描述:给定一个长度为n的数组给一个数k,你可以进行一下两种操作

1.数组中的一个数减1

2.用数组中的一个数赋值给数组中的另一个数

(操作1 2 等价)

要求操作次数最小,使数组的和小于等于k

input:n,k

        a1,a2,,,an

output:

        ans(最小操作数,)

example:
1 10
20

0

7 8
1 2 1 3 1 2 1
2

题目分析:这题我最开始想的是二分答案,即找到符合答案的最小值,但是check()函数,写的不对。看了大佬的代码才明白,是贪心+模拟,而且思想非常妙。

首先,数组应该sort排序,操作1减,操作2换,在操作数最小,而且数组sum减少的最多,那么应该是不断的拿最小的数a[1]去换最后面的大数,那么减1的操作的功能性就体现出来了,只给a[1]即最小的数减1,拿这个减1后的数去换后面的大数,这样sum应当是减少的最多的,且操作数也小,最后,做一个前缀和处理,不断的去模拟换和减并比较结果记录答案。(换数较好模拟,减1操作其实虚拟模拟,这也是大佬的代码的巧妙之处)。

代码简短,但隐含的意义的思想都很妙。

#include<iostream>
#include<string>
#include<memory.h>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
#include<functional>
#include<cmath>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 2e5 + 7;
const int inf = 0x3f3f3f3f;
ll s[N], pre[N];
ll n, k, sum;

void solve()
{
    memset(pre, 0, sizeof(pre));

    ll ans = 2e18;
    cin >> n >> k;
    for (int i = 1; i <= n; i++) cin >> s[i];
    sort(s + 1, s + 1 + n);
    for (int i = 1; i <= n; i++) pre[i] = pre[i - 1] + s[i];//前缀和处理
    for (int i = 1; i <= n; i++)
    {
        ll now = pre[i] + (n - i) * s[1];//先假设拿a[1]换掉最后n-i个数可行
        if (now <= k) ans = min(ans, ll(n - i));//如果可行,直接比较答案
        else//换掉最后n-i数不可行,那么补救的方案就是使a[1]减1
        { //te表示应当再使a[1]减几次1才可行。now-k表示多余的要减掉的,这些多余要减的,
            ll te = ceil((now - k) * 1.0 / (n - i + 1));//分给a[1]和后n-i个数,就等于每1个要减的数,
          //此处即虚拟模拟减1操作                      即a[1]要减的数。ceil向上取整
                                                // 如多余的数为5,而且只拿a[1]换最后一个5/2=2
                                           //但使a[1]减2,整体才减4,所以应当减3,整体减6
            ans = min(ans, ll(n - i + te));
        }
    }
    cout << ans << endl;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--)
        solve();
    return 0;

}
/*


 */

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值