K-periodic Garland from Codeforces Round #642 (Div. 3)E


题目大意:给一个01串,可以将0变成1,1变成0,每次变换记为1次操作,问最终让每个1之前的距离都为k,需要最少多少次操作

数据范围1e6,很明显的一个复杂度o(n)的一个dp

做题思路是从已知的合法串递推出更长的合法串

首先处理左边到i位置为止左边所有位置合法的状态:dp(i) = min( sum[i-1] , dp[i-k] ) + ( i == ' 1 ' ? 0 : 1 ) ;

最终统计答案的时候再从后往前更新,让每一个dp(i)加上i之后1的个数,将i之后所有的1变成0,即为合法状态。

#pragma GCC optimize(2)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<string>
#define ll long long
using namespace std;
const int inf = 1e9;

int main()
{
    int t;
    cin >> t;
    string s;
    while (t--)
    {
        int n, k;
        cin >> n >> k;
        string s;
        cin >> s;
        s = '0' + s;
        vector<int>dp(n + 1, inf);//最初的dp数组中每个位置的操作数都不合法
        vector<int>sum(n + 1, 0);
        if (n == 1) { cout << "0" << endl; continue; }//特判n==1时的情况
        for (int i = 1; i <= n; i++)//预处理前缀和
        {
            sum[i] = sum[i - 1];
            if (s[i] == '1')
                sum[i]++;
        }
        //cout << endl;
        int ans = sum[n];//ans初始化为将所有1全部变成0

        //dp[i]记录到i位置位置全部合法所需要的操作数

        for (int i = 1; i <= n; i++)
        {
            dp[i]= sum[i - 1];
            if (i - k >= 1)
            {
                dp[i] = min(dp[i], dp[i - k] + (sum[i - 1] - sum[i - k]));
            }
            if (s[i] == '0')dp[i]++;
        }

        int num = 0;
        for (int i = n; i >= 1; i--)
        {
            ans = min(ans, dp[i] + num);//将i位置之后的1全部清除也得纳入答案决策内
            if (s[i] == '1')
                num++;
        }
        cout << ans << endl;
    }
    return 0;

}
posted @ 2020-05-21 09:03  Lecoww  阅读(112)  评论(0编辑  收藏  举报