zoukankan      html  css  js  c++  java
  • 题解 CF1353E K-periodic Garland

    CF1353E K-periodic Garland

    由题意,每个位置上有且只有 (0/1) 两种状态,且我们若是求出前缀和就能快速得出其中某一段中 (1) 的个数。

    首先看一下如果让我们构造怎么构造。我们要构造一个 (1) 之间距离恰好为 (k) 的序列,就是说位置上的状态每次转移到 (1) ,都必须转移回到 (0) ,在 (0) 的状态呆 (k) 次再转移回 (1) 状态。

    画出状态机如下:

    由这个图我们先得出一个暴力且错误的状态转移方程:

    (f(i)=f(i-k)+sum(i-1)-sum(i-k)+[s_i=0]),其中(f(i)) 表示构造第一位是 (1) 的序列需要改造的步数 ,(sum) 是原来序列的前缀和,(s_i) 是原序列。

    这个东西为什么错误?因为我们不敢说第一位一定是 (1),说不定答案的序列开头很长一段都是 (0)。我们首先想到,这个问题可以大力枚举前面 (0) 的位数解决。假设我们把前 (i-1) 位设为 (0),第 (i) 位设成 (1) 的代价为 (g(i)),我们很容易得到:(g(i)=sum(i-1)+[s_i=0])

    我们修改状态 (f(i)) 为到第 (i) 位为 (1) 时,前面均合法且至少有一个 (1) 的最小代价。

    那么我们可以得到状态转移方程:(f(i)=min( f(i-k), g(i-k) )+sum(i-1)-sum(i-k)+[s_i=0])

    我们的后缀仍然有可能全是 (0) ,所以统计答案时,我们枚举构造序列长度 (i) ,同时维护一个值 (z) 表示将以 (i+1) 为起点的后缀全部设为 (0) 的代价。答案就是 (Min_{i=1}^nmin( f(i) , g(i) )+z(i))

    个人感觉难度 绿-蓝

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=4e6+10;
    int f[N],g[N],sum[N];
    char str[N];
    
    int main()
    {
    	int t; cin>>t;
    	while(t--)
    	{
    		int n,k;
    		cin>>n>>k>>(str+1);
    		for(int i=0;i<=n;i++) f[i]=0x3f3f3f3f,sum[i]=0;
    		for(int i=1;i<=n;i++)
    			sum[i]=sum[i-1]+(str[i]-'0'), g[i]=sum[i-1]+int(str[i]=='0');
    		for(int i=1;i<=n;i++)
    			if(i>k) f[i]=min(f[i-k],g[i-k])+sum[i-1]-sum[i-k]+int(str[i]=='0');
    		int ans=sum[n];
    		for(int i=1;i<=n;i++)
    			ans=min(ans,min(f[i],g[i])+sum[n]-sum[i]);
    		cout<<ans<<'
    ';
    	}
    	return 0;
    }
    
  • 相关阅读:
    数组练习1
    学习进度04
    求一个数组的最大子数组之和02
    求一个数组的最大子数组之和01
    学习进度03
    四则运算的功能添加版02
    第二周学习进度
    Right-BICEP 测试四则运算程序
    实现四则运算的功能添加版01
    本周实验PSP0 过程文档
  • 原文地址:https://www.cnblogs.com/IzayoiMiku/p/14646647.html
Copyright © 2011-2022 走看看