[题解]AT_abc325_g [ABC325G] offence
思路
看到消除区间,并且 \(|S| \leq 300\) 果断选择区间 DP。
定义 \(dp_{i,j}\) 表示操作 \(i \sim j\) 区间能剩下最少数量。
首先枚举 \(i,j\),然后枚举一个分解点 \(k\),那么显然有 \(dp_{i,j} = \min_{i \leq k < j}\{dp_{i,k} + dp_{k + 1,j}\}\)。
如果 \(s_i\) 是 o,需要单独考虑一下。
依旧在 \(k \in (i,j]\) 寻找 \(s_k\) 为 f 的 \(k\),如果 \(dp_{i + 1,k - 1}\) 不为 \(0\),表明 \((i + 1) \sim (k - 1)\) 不能完全操作掉,因此无法使得 \(i,k\) 挨在一起,形成 of。
所以当我们找到满足 \(dp_{i + 1,k - 1} = 0\) 的 \(k\) 时,\(i \sim k\) 中的所有元素都能被消除,并且可以在 \(k\) 之后顺手消除 \(K\) 个,所以直接将 \(dp_{i,j}\) 设为 \(\min(dp_{i,j},\max(dp_{k + 1,j} - K,0))\) 即可。
Code
#include <bits/stdc++.h>
#define re register
using namespace std;
const int N = 310;
int n,m;
int dp[N][N];
string s;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> s >> m;
n = s.size();
s = ' ' + s;
for (re int i = 1;i <= n;i++){
for (re int j = i;j <= n;j++) dp[i][j] = j - i + 1;
}
for (re int l = 2;l <= n;l++){
for (re int i = 1;i + l - 1 <= n;i++){
int j = i + l - 1;
if (l == 2){
if (s[i] == 'o' && s[j] == 'f') dp[i][j] = 0;
}
else{
for (re int k = i;k < j;k++) dp[i][j] = min(dp[i][j],dp[i][k] + dp[k + 1][j]);
if (s[i] == 'o'){
for (re int k = i + 1;k <= j;k++){
if (s[k] == 'f' && !dp[i + 1][k - 1]) dp[i][j] = min(dp[i][j],max(dp[k + 1][j] - m,0));
}
}
}
}
}
cout << dp[1][n];
return 0;
}

浙公网安备 33010602011771号