• CF1353E 【K-periodic Garland】


    题目大意

    给你一个长度为 (n)(01) 字符串,要求让这个字符串的每个 (1) 字符之间的距离恰好都为 (k) ,问至少要修改几个字符。

    思路

    显然这是道 dp 题。

    • (dp_{i ,0}) 表示到第 (i) 个字符为止,只让第 (i) 位为 (1),其余 (i-1) 位均位 (0) 时需要的操作次数。

    • (dp_{i,1}) 表示到第 (i) 个字符为止,让第 (i) 位为 (1),且保证在这之前合法所需要的最少操作次数。

    那显然,只要维护一个前缀和 (sum_i) ,统计到第 (i-1) 个字符位置 (1) 的数量,那么:

    [dp_{i,0}=egin{cases}sum_{i-1} (s_i=1)\sum_{i-1}+1 (s_i=0)end{cases} ]

    Ps: (s_i) 表示字符串的第 (i) 位所表示的字符。

    同时,我们也很容易推出另外一个转移式:

    [mathop{dp_{i,1}}limits_{i-kge1}=egin{cases}min(dp_{i-k,1},dp_{i-k,0}) + sum_{i-1}-sum_{i-k} (s_i=1)\min(dp_{i-k,1},dp_{i-k,0}) + sum_{i-1}-sum_{i-k}+1 (s_i=0)end{cases} ]

    这很好理解,只要保证第 (i-k) 个位置为 (1) 并将 ((i-k+1)sim (i-1)) 中的所有 (1) 都设为 (0) 即可,这步同样可以用前缀和维护。

    最后计算答案的时候,将 (dp) 数组从后往前扫一遍,每次在 (dp_{i,0};,;dp_{i,1}) 中取 (min) 。在扫的同时维护一个 (num) 表示从 ((i+1) sim n) 这段中 (1) 的数量,统计答案的时候要加上这个 (num),意味着吧 ((i+1) sim n) 中所有的 (1) 设为 (0),防止出现不合法的情况。

    时间复杂度:( ext{O}(n))

    Code

    #include<bits/stdc++.h>
    
    using namespace std;
    
    int read()
    {
    	int ans=0,f=1;
    	char c=getchar();
    	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    	while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    	return ans*f;
    }
    
    const int N=1e6+5,inf=0x7f7f7f7f;
    int t,n,k,dp[N][2],sum[N],ans;
    char s[N]; 
    
    int main()
    {
    	memset(dp,0x7f,sizeof(dp));
        // 将 dp 数组初始化,将所有可能先设为不合法
    	t=read();
    	while(t--)
    	{
    		n=read();k=read();
    		scanf("%s",s+1);
    		for(int i=1;i<=n;++i)
            // 统计前缀和
    			sum[i]=sum[i-1]+(s[i]-'0');
            ans=sum[n];  // 这里的意思是把答案初始化为:把所有的 1 都变成 0 所需要的操作次数
    		for(int i=1;i<=n;++i)
    		{
    			dp[i][0]=sum[i-1];
                // dp[i][0] 的转移
    			if(i-k>=1)
    				dp[i][1]=min(dp[i-k][1]+sum[i-1]-sum[i-k],dp[i-k][0]+sum[i-1]-sum[i-k]);
                // dp[i][1] 的转移
    			if(s[i]=='0')
    			{
    				if(dp[i][0]!=inf)
    					dp[i][0]++;
    				if(dp[i][1]!=inf)
    					dp[i][1]++;
    			}
    				
    		}
    		int num=0;
            // 统计答案的时候维护一个 num,防止出现不合法情况
    		for(int i=n;i>=1;--i)
    		{
    			ans=min(ans,min(dp[i][1]+num,dp[i][0]+num));
    			if(s[i]=='1')
    				num++;
    		}
    			
    		for(int i=1;i<=n;++i)
            // 这是这题的一个坑点,直接 memset 会 T,必须手动清空
    		{
    			sum[i]=0;
    			dp[i][0]=inf;
    			dp[i][1]=inf;
    		} 
    		printf("%d
    ",ans);
    	}
    	return 0;
    } 
    
    
  • 相关阅读:
    sql知识
    铁道部新客票系统设计(三)
    PYTHON压平嵌套列表
    快速升级App支持iOS6及iPhone5的4寸屏幕
    TreeListView
    杭州ADC技术嘉年华两日总结SOA,去C
    .NET(C#): Task.Unwrap扩展方法和async Lambda
    关于分布式系统的数据一致性问题
    wcf 随笔1
    Linux进程基础
  • 原文地址:https://www.cnblogs.com/blackbird137/p/13550405.html
Copyright © 2020-2023  润新知