Luogu P1136 迎接仪式 (线性DP,细节)
- 较强限制:"jz"才有贡献; 交换操作。
- 考虑DP:考虑缩小规模->那么dp数组要维护一个 前i个字母,交换了k次,当前的数交不交换。但是如果 pos1和pos2是互相交换的,但是这俩位置会计入k两次,所以把交换了k次变成'j'字母交换了j次,'z'字母交换了l次数。这个状态方程好像没问题,但是转移过程中会出现非法转移:
f[i][0][0][0] = max(f[i][0][0][0], f[i - 1][0][0][1]); //j,l都为0, i-1是翻转的,这对么? - 不再使用当前位是否翻转这个状态,换一个更为稳妥地:
我们设f[i][j][k][0]为遍历了字符串的前ii位,改变了jj个jj和kk个zz,并且当前的这一位为jj所能达到的最大价值
设f[i][j][k][1]为遍历了字符串的前ii位,改变了jj个jj和kk个zz,并且当前的这一位为zz所能达到的最大价值
- 这题最终j和k必须用的次数一样多才合法,要是f[0][100][100][0] 这样肯定是不对的,后面状态从此转移的话j实际用了50次,k90次,也是会被转移到k=100,j =100的,起始合法状态只有f[0][0][0][1] = 0; (0位其实是没有字母的让他为z也不会有贡献)
总结:1. 明确方程的定义!: 容易想出的方程可能会有非法转移,考虑到它。或者更改方程。
2. 本题中jk必须相等,所以要规定唯一合法起始
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false) ,cin.tie(0), cout.tie(0);
//#pragma GCC optimize(3,"Ofast","inline")
#define ll long long
typedef __int128_t li;
//#define int long long
#define PII pair<int, int>
const int N = 5e2 + 5;
const int M = 1e2 + 5;
const int INF = 0x3f3f3f3f;
const ll LNF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
const double PI = acos(-1.0);
int f[N][M][M][2];
int main () {
int n, k; cin >> n >> k;
string ss; cin >> ss; ss = "*" + ss;
memset(f, -0x3f, sizeof f);
f[0][0][0][1] = 0;
int ans = 0;
for ( int i = 1; i <= n; ++ i ) {
for ( int j = 0; j <= k; ++ j ) {
for ( int l = 0; l <= k; ++ l ) {
if(ss[i] == 'z') {
f[i][j][l][1] = max(f[i - 1][j][l][1], f[i - 1][j][l][0] + 1);
if(l) f[i][j][l][0] = max(f[i - 1][j][l - 1][0], f[i - 1][j][l - 1][1]);
} else {
f[i][j][l][0] = max(f[i - 1][j][l][1], f[i - 1][j][l][0]);
if(j) f[i][j][l][1] = max(f[i - 1][j - 1][l][0] + 1, f[i - 1][j - 1][l][1]);
}
}
}
}
for(int i=0;i<=k;i++){
ans=max(ans,max(f[n][i][i][1],f[n][i][i][0]));
}
cout << ans << endl;
return 0;
}