cf1353E. K-periodic Garland(字符串的dp)

题目链接:https://codeforces.com/problemset/problem/1353/E

题意: 

  • 给你一个长度为n的01串s, 和一个正整数k。
  • 操作:每次操作将串中的0变成1,或者将串中的1变成0
  • 问:至少需要操作几次,才能使得传中所有相邻1下标之差等于k?(如果只有一个1或者没有1 也满足)

思路:

  • 原问题:至少操作几次,才能使得1 ~ n(整个串)所有相邻’1‘下标之差等于k。
  • 子问题:至少操作几次,才能使得1 ~ i 的子串所有相邻’1‘下标之差等于k。
  • dp[i][0]表示将第i位变成’0‘,使得1~i的子串满足条件,dp[i][1]表示将第i位变成’1‘,使得1~i的子串满足条件。
  • sum[i]前缀和,表示1到i 有多少个‘1’。
  • 将第i位变成‘0‘,不会影响1 ~ i子串的合法性,只需要继承上一个状态较小值就好了,即min(dp[i-1][0],dp[i-1][1])。
  • 将第i位变成’1‘,两种合法情况。
  1. 上一个’1‘的位置在第i - k位,那么就是i - k + 1到i - 1的‘1’都要改成‘0’,并且将第i-k位也改成’1‘,即sum[i-1]-sum[i-k+1] + dp[i - k][1]
  2. i之前没有’1‘,那么就是1到i-1的’1‘都要改成’0‘,即sum[i-1]
  • 转移方程:
  • dp[i][0] = min(dp[i -1][0], dp[i - 1][1]) + (a[i] == '1');//如果当前是’1‘,就要修改为’0‘,修改次数+1
  • dp[i][1] = min(sum[i-1], dp[i-k][1] + sum[i-1] - sum[i-k]) + (a[i] == '0);//如果当前是’1‘,就要修改成‘1’,修改次数+1
  • 最后答案就是dp[n][0]和dp[n][1]之中较小者。

ac代码:

#include <bits/stdc++.h>
#define LL __int128
#define ll long long
#define sc(a) scanf("%d", &a)
#define sc2(a, b) scanf("%d%d", &a, &b)
#define sc3(a, b, c) scanf("%d%d%d", &a, &b, &c)
#define scl(a) scanf("%lld", &a)
#define scl2(a, b) scanf("%lld%lld", &a, &b)
#define ss(a) scanf("%s", a)
#define mem(a, b) memset(a, b, sizeof(a))
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define PII pair<int, int>
using namespace std;
const int maxn = 1e6 + 5;
char a[maxn];
int dp[maxn][2], sum[maxn];
int main(){
	int t, n, k;
	sc(t);
	while(t --){
		sc2(n, k);
		ss(a + 1);
		for(int i = 1; i <= n; i ++){
			dp[i][0] = dp[i][1] = 0;
			sum[i] = sum[i - 1] + a[i] - '0';
		}
		for(int i = 1; i <= n; i ++){
			dp[i][0] = min(dp[i - 1][0], dp[i - 1][1]) + (a[i] == '1');
			dp[i][1] = min(sum[i - 1], dp[max(i - k, 0)][1] + sum[i - 1] - sum[max(i - k, 0)]) + (a[i] == '0');
			//当i-k<0时,表示i之前就不存在‘1’了,可以当他是0,对答案没有影响.
		}
		printf("%d\n",min(dp[n][0], dp[n][1]));
	}
	return 0;
}
/*
6
9 2
010001010
9 3
111100000
7 4
1111111
10 3
1001110101
1 1
1
1 1
0

1
2
5
4
0
0
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值