2024 CCPC 河北省大学生程序设计竞赛 D题思路分享(dp,组合计数)
题意概述
一个字符串是优美的当且仅当:长度为 \(4\),第 \(1,2,4\) 个字符相同,第 \(3\) 个字符与其他不同。
给定一个长度为 \(n\),仅由 \(c\) 和 \(p\) 构成的字符串 \(s\) ,可以花费 \(1\) 元交换两个相邻位置。
求恰好花费 \(m\) 元,得到的字符串中优美子序列的最大值。
\(1\le n,m \le 500\)。
思路
考虑 \(dp\),分析需要哪些状态。
优美字符串为 \(ccpc\) 或 \(ppcp\)。
对于每个 \(c\),假设它之前有 \(j\) 个 \(p\),总共有 \(cp\) 个 \(p\),贡献为 \(j \times(j-1)/2\times(cp-j)\),这样构成 \(ppcp\)。对于 \(c\) 同理。
因此需要存之前有多少个 \(p\)。
假设初始字符串所有 \(p\) 的位置为 \(a_1,a_2\cdots a_{cp}\),最终字符串所有 \(p\) 的位置为 \(b_1,b_2,\cdots b_{cp}\),最少交换次数为 \(\sum_{i=1}^{cp}{|a_i-b_i|}\),记为 \(k\) 。
如果交换完钱还多,可以重复交换两次任意相邻元素,因此 \(k\) 需要满足 \(k \le m\),同时奇偶性相同。
第三维存 \(k\),每次转移讨论选 \(c\) 还是 \(p\)。
记 \(dp[i][j][k]\) 表示枚举到第 \(i\) 位,前面有 \(j\) 个 \(p\),最少交换次数为 \(k\),对应的最大答案。
-
第 \(i\) 位选 \(c\),\(j\) 和 \(k\) 都不变。
-
第 \(i\) 位选 \(p\),\(j\) 增加 \(1\),\(k\) 增加 \(s\) 中第 \(j+1\) 位 \(p\) 的位置与 \(i\) 的绝对值。这是可以预处理的。
时间复杂度 \(\mathcal{O}(n^2m)\)。
代码
//author:kzssCCC
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll INF = 9e18;
void solve(){
string s;
cin >> s;
int n = s.size();
int m;
cin >> m;
s = ' '+s;
int cc = count(s.begin()+1,s.end(),'c');
int cp = n-cc;
vector<int> pos(cp+1);
int tot = 1;
for (int i=1;i<=n;i++){
if (s[i]=='p'){
pos[tot++] = i;
}
}
vector<vector<vector<ll>>> dp(n+1,vector<vector<ll>>(cp+1,vector<ll>(m+1,-INF)));
dp[0][0][0] = 0;
for (int i=1;i<=n;i++){
for (int j=0;j<=cp;j++){
for (int k=0;k<=m;k++){
if (dp[i-1][j][k]==-INF) continue;
dp[i][j][k] = max(dp[i][j][k],dp[i-1][j][k]+(ll)j*(j-1)/2*(cp-j));
if (j+1<=cp){
int nk = k+abs(pos[j+1]-i);
if (nk<=m){
dp[i][j+1][nk] = max(dp[i][j+1][nk],dp[i-1][j][k]+(i-1ll-j)*(i-2ll-j)/2*(cc-(i-1-j)));
}
}
}
}
}
ll res = 0;
for (int k=0;k<=m;k++){
if (dp[n][cp][k]!=-INF && (k&1)==(m&1)){
res = max(res,dp[n][cp][k]);
}
}
cout << res << '\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
// cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号