版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013578420/article/details/82385021
题目链接
https://vjudge.net/contest/248767#problem/A
题目大意
给你一个只含字符’D’和’G’的字符串S , 每次可以选择一个子区间翻转(‘D’变成’G’, ‘G’变成’D’),费用为1。 求最少费用, 使得存在至少n个长度至少为k的极大’D’子串 。
题目思路
很容易想到dp, f[i][j][k][rev], 表示考虑前i个字符, 当前满足条件的有j段, 后缀’D’的长度为k, 第i个点翻转状态为rev, 进行转移即可。 这个dp乍一看像是O(n^3)的, 实际上当|S|
Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <bitset>
#include <map>
#include <stack>
#include <set>
#define ls ch[x][0]
#define rs ch[x][1]
#define ll long long
#define pi pair<int, int>
#define mp make_pair
#define fi first
#define se second
using namespace std;
int gi(){
int ret = 0; char c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)){
ret = ret * 10 + c - '0';
c = getchar();
}
return ret;
}
const int N = (int)2020 + 10;
const int M = (int)2e7 + 10;
int m, k, n; char s[N];
int f[M];
int id(int i, int j, int h, int rev){
return rev + h * 2 + j * (m + 1) * 2 + i * (k + 1) * (m + 1) * 2;
}
void update(int &x, int y){
if (y == -1) return;
if (x == -1) x = y;
else x = min(x, y);
}
int main(){
scanf("%d %d", &m, &k);
scanf("%s", s + 1); n = strlen(s + 1);
if (n < m * k){puts("-1"); return 0;}
f[id(0, 0, 0, 0)] = 0;
for (int i = 1; i < M; i ++) f[i] = -1;
for (int i = 0; i < n; i ++)
for (int j = 0; j <= k; j ++)
for (int h = 0; h <= m; h ++)
for (int rev0 = 0; rev0 <= 1; rev0 ++)
for (int rev1 = 0; rev1 <= 1; rev1 ++){
int dw = rev1 && !rev0;
int dh = s[i + 1] == (rev1 ? 'G' : 'D') ? min(h + 1, m) : 0;
int dj = h == m - 1 && dh == m;
int v = id(i + 1, min(j + dj, k), dh, rev1);
int u = id(i, j, h, rev0);
if (f[u] == -1) continue;
update(f[v], f[u] + dw);
}
int ans = -1;
for (int h = 0; h <= m; h ++)
for (int rev = 0; rev <= 1; rev ++)
update(ans, f[id(n, k, h, rev)]);
printf("%d\n", ans);
return 0;
}