题意
给定一个长度为
n
n
n的01串,最多可以将
m
m
m个0变成1,要求使01串中第
k
k
k长连续1串的长度最大。
输出最大值,如果无法得到第
k
k
k长的连续1串,输出-1
做法
考虑二分答案,二分第
k
k
k长连续1串的长度。
用DP来check
对于当前的
t
a
r
tar
tar, 设
d
p
[
i
]
[
j
]
[
0
/
1
]
dp[i][j][0/1]
dp[i][j][0/1]为前
i
i
i个字符,有
j
j
j段长度大于等于
t
a
r
tar
tar的连续1串,且当前字符是否位于第
j
j
j段连续1串的末尾(0为假1为真)所需的最少操作次数。
初始有 d p [ 0 ] [ 0 ] [ 0 ] = 1 dp[0][0][0] = 1 dp[0][0][0]=1, 其余赋最大值。
对于转移:
- 若当前字符为1,那么有
d p [ i ] [ j ] [ 0 ] = min ( d p [ i − 1 ] [ j ] [ 0 ] ) , dp[i][j][0] = \min(dp[i-1][j][0]), dp[i][j][0]=min(dp[i−1][j][0]),
d p [ i ] [ j ] [ 1 ] = min ( d p [ i − 1 ] [ j ] [ 1 ] ) dp[i][j][1] = \min(dp[i-1][j][1]) dp[i][j][1]=min(dp[i−1][j][1]).- 第一个式子不带 d p [ i − 1 ] [ j ] [ 1 ] dp[i-1][j][1] dp[i−1][j][1]是因为 d p [ i − 1 ] [ j ] [ 1 ] dp[i-1][j][1] dp[i−1][j][1]说明第 j j j段的末尾是 i − 1 i-1 i−1, 加上当前字符一定是1,那么这一段的末尾肯定是 i i i了,和第三维的0矛盾。
- 第二个式子不带 d p [ i − 1 ] [ j ] [ 0 ] dp[i - 1][j][0] dp[i−1][j][0]是因为对于 d p [ i − 1 ] [ j ] [ 0 ] dp[i - 1][j][0] dp[i−1][j][0], 我们不知道第 j j j段的末尾具体在哪,若要让第 j j j段的末尾为 i i i, 我们不知道是否要进行0变1操作,所以不能转移。
- 若当前字符为0,那么有
d p [ i ] [ j ] [ 0 ] = min ( d p [ i − 1 ] [ j ] [ 0 ] , d p [ i − 1 ] [ j ] [ 1 ] ) , dp[i][j][0] = \min(dp[i - 1][j][0], dp[i - 1][j][1]), dp[i][j][0]=min(dp[i−1][j][0],dp[i−1][j][1]),
d p [ i ] [ j ] [ 1 ] = min ( d p [ i − 1 ] [ j ] [ 1 ] + 1 ) dp[i][j][1] = \min(dp[i - 1][j][1] +1) dp[i][j][1]=min(dp[i−1][j][1]+1).- 第一个式子我们只需要第 j j j段的末尾不在 i i i, 那末尾可以在 i − 1 i-1 i−1, 也可以不在 i − 1 i-1 i−1,所以两个都可以转移。
- 第二个式子要求第 j j j段末尾在 i i i, 那么可以在第 j j j段末尾在 i − 1 i-1 i−1的基础上加一次操作,让第 i i i个字符变成1.同理对于 d p [ i − 1 ] [ j ] [ 0 ] dp[i-1][j][0] dp[i−1][j][0], 我们不知道第 j j j段具体末尾在哪,所以无法转移。
- 若
i
≥
t
a
r
i \ge tar
i≥tar且
j
≥
1
j \ge 1
j≥1, 有
d p [ i ] [ j ] [ 1 ] = min ( d p [ i − t a r ] [ j − 1 ] [ 0 ] + v a l [ i − t a r + 1 , i ] ) dp[i][j][1] = \min(dp[i - tar][j - 1][0] +val[i - tar +1, i]) dp[i][j][1]=min(dp[i−tar][j−1][0]+val[i−tar+1,i])- 即新增一段长度大于等于 t a r tar tar的连续1串,操作次数为前 i − t a r i-tar i−tar个字符、有 j − 1 j-1 j−1段连续1串,且末尾不在 i − t a r i-tar i−tar所需的修改次数加上 i − t a r + 1 i-tar+1 i−tar+1到 i i i的字符0的个数。
最后只需要判断一下 min ( d p [ n ] [ k ] [ 0 ] , d p [ n ] [ k ] [ 1 ] ) \min(dp[n][k][0], dp[n][k][1]) min(dp[n][k][0],dp[n][k][1])是否小于等于 m m m即可。
代码
#include <bits/stdc++.h>
using namespace std;
int dp[200003][8][2];
int main() {
cin.tie(0) -> sync_with_stdio(0);
int n, m, k;
string s;
cin >> n >> m >> k >> s;
vector<int> cnt_0(n + 1);
s.insert(0, " ");
for (int i = 1; i <= n; ++i)
cnt_0[i] = cnt_0[i - 1] + (s[i] == '0');
int l = 0, r = 2e5;
auto check = [&](int tar) {
for (int i = 0; i <= n; ++i)
for (int j = 0; j <= k; ++j) dp[i][j][0] = dp[i][j][1] = 0x3f3f3f3f;
dp[0][0][0] = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= k; ++j) {
if (s[i] == '1') {
dp[i][j][0] = min(dp[i][j][0], dp[i - 1][j][0]);
dp[i][j][1] = min(dp[i][j][1], dp[i - 1][j][1]);
}
else {
dp[i][j][0] = min({dp[i][j][0], dp[i - 1][j][0], dp[i - 1][j][1]});
dp[i][j][1] = min(dp[i][j][1], dp[i - 1][j][1] + 1);
}
if (j >= 1 && i >= tar && dp[i - tar][j - 1][0] != 0x3f3f3f3f)
dp[i][j][1] = min(dp[i][j][1], dp[i - tar][j - 1][0] + cnt_0[i] - cnt_0[i - tar]);
}
}
// for (int i = 1; i <= n; ++i) {
// for (int j = 0; j <= k; ++j) {
// cerr << i << ' ' << j << ' ' << 0 << ' ' << dp[i][j][0] << '\n';
// cerr << i << ' ' << j << ' ' << 1 << ' ' << dp[i][j][1] << '\n';
// }
// }
return min(dp[n][k][0], dp[n][k][1]) <= m;
};
// check(3);
while (l < r) {
int mid = (l + r + 1) >> 1;
// cerr << l << ' ' << r << ' ' << mid << '\n';
if (check(mid)) l = mid;
else r = mid - 1;
}
if (!l) cout << "-1\n";
else cout << l << '\n';
return 0;
}