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;
}
posted @ 2026-05-15 16:27  kzssCCC  阅读(10)  评论(0)    收藏  举报