Codeforces Round #642 (Div. 3) E. K-periodic Garland (dp or 贪心)

E. K-periodic Garland

题意

给定一个01串,要求这个串中所有两个相邻1之间的距离必须为 k ( r − l = = k ) k(r-l==k) k(rl==k),求最少的操作次数。

思路1

定义dp[i][0]为前i个合法,并且第i个为0时的最少操作次数。
定义dp[i][1]为前i个合法,并且第i个为1时的最少操作次数。
那么有,
d p [ i ] [ 0 ] = m i n ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] ) + ( s [ i ] = = ′ 1 ′ ) dp[i][0] = min(dp[i-1][0],dp[i-1][1])+(s[i]=='1') dp[i][0]=min(dp[i1][0],dp[i1][1])+(s[i]==1)
d p [ i ] [ 1 ] = m i n ( p r e [ i − 1 ] , d p [ i − k ] [ 1 ] + p r e [ i − 1 ] − p r e [ i − k ] ) + ( s [ i ] = = ′ 0 ′ ) ; dp[i][1] = min(pre[i-1],dp[i-k][1]+pre[i-1]-pre[i-k])+(s[i]=='0'); dp[i][1]=min(pre[i1],dp[ik][1]+pre[i1]pre[ik])+(s[i]==0);
dp[i][0]合法只需要前i-1合法,dp[i][1]合法需要dp[i-k][1]合法并且它们之间全为0,或者前i-1全为0。

代码1

#include 

using namespace std;

int t;
int n,k;


int main()
{
    cin >> t;
    while(t--){
        cin >> n >> k;
        int dp[n+5][2]= {0};
        string s;
        cin >> s;
        s = " "+s;
        int pre[n+5]= {0};
        for(int i = 1; i <= n; ++i)
            pre[i] = pre[i-1]+s[i]-'0';
        for(int i = 1; i <= n; ++i){
            int p = max(0,i-k);
            dp[i][0] = min(dp[i-1][0],dp[i-1][1])+(s[i]=='1');
            dp[i][1] = min(pre[i-1],dp[p][1]+pre[i-1]-pre[p])+(s[i]=='0');
        }
        int ans = min(dp[n][0],dp[n][1]);
        cout << ans << endl;
    }
    return 0;
}

思路2

直接抹掉所有1,所需要的次数即1的个数,记为sum。
然后枚举区间[1,k],让它往后扩展。就是在这个串里以间隔k来填充1,用cnt记录我们可以减去的代价,如果当前位置本来就是1,则说明需要-1,如果是0,则需要+1。
记录出现的1和0,然后对ans一步步优化。

代码2

#include 

using namespace std;

int t;
int n,k;

int main()
{
    cin >> t;
    while(t--){
        cin >> n >> k;
        int dp[n+5][2]={0};
        string s;
        cin >> s;
        s = " "+s;
        int sum = 0;
        for(int i = 1; i <= n; ++i)
            sum += (s[i]=='1');
        int ans = sum;
        for(int i = 1; i <= k; ++i){
            int cnt = 0;
            for(int j = i; j <= n; j += k){
                if(s[j]=='1')
                    cnt--;
                else
                    cnt++;
                //当cnt>0时,将cnt变为0,意味着直接将前面的全部变成0,因为此时将0变为1已经不划算了
                cnt = min(cnt,0);
                ans = min(ans,sum+cnt);
            }
        }
        cout << ans << endl;
    }
    return 0;
}

你可能感兴趣的:(Codeforces)