3086. 拾起 K 个 1 需要的最少行动次数 Hard

给你一个下标从 0 开始的二进制数组 nums,其长度为 n ;另给你一个 正整数 k 以及一个 非负整数 maxChanges 。

Alice 在玩一个游戏,游戏的目标是让 Alice 使用 最少 数量的 行动 次数从 nums 中拾起 k 个 1 。游戏开始时,Alice 可以选择数组 [0, n - 1] 范围内的任何索引 aliceIndex 站立。如果 nums[aliceIndex] == 1 ,Alice 会拾起一个 1 ,并且 nums[aliceIndex] 变成0(这 不算 作一次行动)。之后,Alice 可以执行 任意数量 的 行动包括零次),在每次行动中 Alice 必须 恰好 执行以下动作之一:

 ·选择任意一个下标 j != aliceIndex 且满足 nums[j] == 0 ,然后将 nums[j] 设置为 1 。这个动作最多可以执行 maxChanges 次。

 ·选择任意两个相邻的下标 x 和 y|x - y| == 1)且满足 nums[x] == 1nums[y] == 0 ,然后交换它们的值(将 nums[y] = 1 和 nums[x] = 0)。如果 y == aliceIndex,在这次行动后 Alice 拾起一个 1 ,并且 nums[y] 变成 0 。

返回 Alice 拾起 恰好 k 个 1 所需的 最少 行动次数。

示例 1:

输入:nums = [1,1,0,0,0,1,1,0,0,1], k = 3, maxChanges = 1

输出:3

解释:如果游戏开始时 Alice 在 aliceIndex == 1 的位置上,按照以下步骤执行每个动作,他可以利用 3 次行动拾取 3 个 1 :

 ·游戏开始时 Alice 拾取了一个 1 ,nums[1] 变成了 0。此时 nums 变为 [1,1,1,0,0,1,1,0,0,1] 。

 ·选择 j == 2 并执行第一种类型的动作。nums 变为 [1,0,1,0,0,1,1,0,0,1]

 ·选择 x == 2 和 y == 1 ,并执行第二种类型的动作。nums 变为 [1,1,0,0,0,1,1,0,0,1] 。由于 y == aliceIndex,Alice 拾取了一个 1 ,nums 变为  [1,0,0,0,0,1,1,0,0,1] 。

 ·选择 x == 0 和 y == 1 ,并执行第二种类型的动作。nums 变为 [0,1,0,0,0,1,1,0,0,1] 。由于 y == aliceIndex,Alice 拾取了一个 1 ,nums 变为  [0,0,0,0,0,1,1,0,0,1] 。

请注意,Alice 也可能执行其他的 3 次行动序列达成拾取 3 个 1 。

示例 2:

输入:nums = [0,0,0,0], k = 2, maxChanges = 3

输出:4

解释:如果游戏开始时 Alice 在 aliceIndex == 0 的位置上,按照以下步骤执行每个动作,他可以利用 4 次行动拾取 2 个 1 :

 ·选择 j == 1 并执行第一种类型的动作。nums 变为 [0,1,0,0] 。

 ·选择 x == 1 和 y == 0 ,并执行第二种类型的动作。nums 变为 [1,0,0,0] 。由于 y == aliceIndex,Alice 拾起了一个 1 ,nums 变为 [0,0,0,0] 。

 ·再次选择 j == 1 并执行第一种类型的动作。nums 变为 [0,1,0,0] 。

 ·再次选择 x == 1 和 y == 0 ,并执行第二种类型的动作。nums 变为 [1,0,0,0] 。由于y == aliceIndex,Alice 拾起了一个 1 ,nums 变为 [0,0,0,0] 。

提示:

 ·2 <= n <= 105

 ·0 <= nums[i] <= 1

 ·1 <= k <= 105

 ·0 <= maxChanges <= 105

 ·maxChanges + sum(nums) >= k

题目大意:按照指定规则行动计算拾起k个1所需的最少行动次数。

分析:

(1)拾起1有三种情况:①在数组中otherIndex处的1需要|AliceIndex-otherIndex|次行动才能拾起;②手动生成Alice旁边的1需要2次行动才能拾起;

(2)由(1)可得,当Alice旁边没有1时,优先拾起手动生成的1。设Alice所在处和旁边处的1最多可以有count个,则当Alice旁边的1和maxChanges得到的1能够满足k个1时,即count+maxChanges>=k时,Alice先拾起旁边的1[需行动max(min(count,k)-1,0)次],再拾起手动生成的1(需行动(k-min(count,k))*2次),总共行动max(min(count,k)-1,0)+(k-min(count,k))*2次;当不能满足时,即count+maxChanges<k时,Alice先拾起数组中的k-maxChanges个1,再拾起手动生成的maxChanges个1,其中拾起手动生成的1需要2*maxChanges次行动;

(3)由(2)可知,本题的关键在于计算count+maxChanges<k时,Alice先拾起数组中的k-maxChanges个1所需的最少行动次数。由于从数组中需要拾起的1的个数是固定的,因此可以维护滑动窗口,使每个窗口内有k-maxChanges个1,从而计算在每个窗口中拾起这些1的最少行动次数即可;

(4)由(3)可知,本题的关键转换为如何计算拾起一个滑动窗口中k-maxChanges个1的最少行动次数。由于数组中0的存在使每个滑动窗口长度不一,因此可以将数组中的1都放在1个数组index中,其中index[i]表示第i+1个1在数组中的位置,然后在index数组中进行窗口滑动,就可以保证每个窗口的长度都相同。又因为在每个窗口中Alice站在窗口的中间位置时所需的行动次数最小,无论向左或向右都会使行动次数变大,所以还需维护每个窗口的中间位置mid,然后计算Alice站在mid时的最小行动次数,此行动次数即为当前窗口中拾起k-maxChanges个1的最少行动次数。

class Solution {
public:
    long long minimumMoves(vector<int>& nums, int k, int maxChanges) {
        int count=0;//count维护连续1的长度
        vector<int> index;
        for(int i=0;i<nums.size();++i){
            if(nums[i]){
                index.emplace_back(i);
                count=max(count,1);
                if(i>0&&nums[i]==nums[i-1]){
                    count=max(count,2);
                    if(i>1&&nums[i]==nums[i-2]) count=3;
                }
            }
        }
        count=min(count,k);
        if(k-count<=maxChanges) return (k-count)*2+max(count-1,0);
        int N=index.size(),rest=k-maxChanges;
        long long ans=LLONG_MAX,ans1,ans2,pos;
        vector<long long> sum(N+1,0);//sum[i]维护前i个1所在的位置和
        for(int i=0;i<N;++i) sum[i+1]=sum[i]+index[i];
        for(int l=0,r=rest,mid=rest/2;r<=N;++l,++r,++mid){
            pos=index[mid];
            ans1=(mid-l)*pos-(sum[mid]-sum[l]);
            ans2=sum[r]-sum[mid]-(r-mid)*pos;
            ans=min(ans,ans1+ans2);
        }
        return ans+2*maxChanges;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值