K 连续位的最小翻转次数



在仅包含 的数组 中,一次 位翻转包括选择一个长度为 的(连续)子数组,同时将子数组中的每个 更改为 ,而每个 更改为
返回所需的 位翻转的次数,以便数组没有值为 的元素。如果不可能,返回



如果最左边的元素是 ,那么我们一定要翻转从位置 开始的子数组。类似的,如果最左边的元素是 ,我们不应该翻转从位置 开始的子数组。这证明了我们可以贪心地执行这一过程:在明确是否要反转第一个子数组之后(位置 ),我们可以考虑将数组中第一个元素(值为 )移除,然后重复这个过程。
我们还可以做得更好。每次翻转一个子数组 ,我们可以考虑这样的两种事件:第一种是 “开始事件”,标记位置 为我们翻转子数组的开始,另一种是 “结束事件” ,标记位置 是我们翻转子数组的结束。使用这些事件,我们就可以知道某一个位置被多少个重叠的翻转子数组覆盖了:它的数值等于 “开始事件” 的数量减去 “结束事件” 的数量。
当我们翻转一个子数组的时候,让我们称翻转子数组的下标集合为一个区间。我们将维护一个变量 ,也就是覆盖当前位置的重叠区间数量。我们只关心 取模之后的值。
当我们翻转从 开始的一个区间,我们在位置 创建一个 “结束事件” 的提示,表明在那里要把翻转状态置反。


class Solution {
    public int minKBitFlips(int[] A, int K) {
        int N = A.length;
        int[] hint = new int[N];
        int ans = 0, flip = 0;

        // 当我们翻转子数组形如 A[i], A[i+1], ..., A[i+K-1]
        // 我们可以在此位置置反翻转状态,并且在位置 i+K 设置一个提醒,告诉我们在那里也要置反翻转状态
        for (int i = 0; i < N; ++i) {
            flip ^= hint[i];
            if (A[i] == flip) {  // 我们是否必须翻转从此开始的子数组
                ans++;  // 翻转子数组 A[i] 至 A[i+K-1]
                if (i + K > N) return -1;  // 如果没办法翻转整个子数组了,那么就不可行
                flip ^= 1;
                if (i + K < N) hint[i + K] ^= 1;
            }
        }

        return ans;
    }
}

class Solution {
public:
    int minKBitFlips(vector<int>& A, int K) {
        int sum=0,cnt=0;
        int n=A.size();
        for(int i=0;i<n;i++){
            if(i>=K){
                sum-=A[i-K]/2;
            }
            if((sum+A[i])%2==0){
                if(i+K>n)return -1;
                A[i]+=2;
                sum++,cnt++;
            }
        }
        return cnt;
    }
};