[题解] [CCPC陕西省赛2022 C题 ] Type The String
[CCPC陕西省赛2022 C题 ] Type The String
题目描述#
给定 \(n\) 个字符串,以任意顺序打到文本编辑器里,每个字符串一行,打字的方法有两种:
- 挨个字母打,每个字母的代价是 \(1\)
- 先把已经打的一行复制到现在这一行,然后在任意位置增删任意个字符,复制的代价是给定的固定值 \(k\) , 之后修改的代价每个字母是 \(1\)
求打出所有字符串的最小花费
输入格式#
第一行包括两个整数 \(n(1 \leq n \leq 10^2), k(1 \leq k \leq 10^2)\) 。
接下来的 \(n\) 行,每行包含一个整数 \(l_i(1 \leq l_i \leq 10^2)\) ,和一个长度为 \(l_i\)的字符串 \(S_i\) ,以空格分隔。
字符串中只有小写字母。
输出格式#
输出共一行一个整数,表示最小花费
题解#
考虑以字符串为点,代价为边权建图,对于第2种操作,借助字符串 \(S_i\) 打印 \(S_j\) 的代价可以确定为 \(k + l_i + l_j - lcs(i, j)\),因此可以建立出以代价为边权的完全图。对于第1种操作,可以采用建立虚拟点的方式,所有点到虚拟点均添加一条长度为 \(l_i\) 的边。此时问题就转换成了求该图边权总和最小的联通块,使用 Kruskal 跑最小生成树就行了。
AC代码#
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 103;
int n, k;
struct Str {
char s[MAXN];
int l;
} str[MAXN];
int f[MAXN][MAXN]; // 求lcs的dp数组
inline int lcs(int x, int y) { // dp求lcs
for (int i = 0; i <= str[x].l; i++) {
for (int j = 0; j <= str[y].l; j++) {
f[i][j] = 0;
}
}
for (int i = 1; i <= str[x].l; i++) {
for (int j = 1; j <= str[y].l; j++) {
if (str[x].s[i] == str[y].s[j]) {
f[i][j] = f[i - 1][j - 1] + 1;
} else {
f[i][j] = max(f[i - 1][j], f[i][j - 1]);
}
}
}
int len = f[str[x].l][str[y].l];
return len;
}
int cost(int x, int y) { // 求操作2借助Sx打印Sy的代价
return k + str[x].l + str[y].l - lcs(x, y) * 2;
}
// 用于最小生成树的并查集
int fa[MAXN];
int find(int x) {
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
inline void merge(int x, int y) {
int fx = find(x), fy = find(y);
fa[fx] = fy;
}
// 图论建边
int te;
struct Edge {
int x = -1, y = -1, w = 0;
} e[MAXN * MAXN];
inline void addEdge(int a, int b, int w) {
Edge edge;
edge.x = a, edge.y = b, edge.w = w;
e[++te] = edge;
}
inline bool cmpE(Edge a, Edge b) {
return a.w < b.w;
}
signed main() {
cin >> n >> k;
int ans = 0;
for (int i = 1; i <= n; i++) {
fa[i] = i;
cin >> str[i].l;
for (int j = 1; j <= str[i].l; j++) {
cin >> str[i].s[j];
}
addEdge(0, i, str[i].l); // 操作1建边
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j < i; j++) {
addEdge(i, j, cost(i, j)); // 操作2建边
}
}
// Kruskal
sort(e + 1, e + 1 + te, cmpE);
for (int i = 1; i <= te; i++) {
int x = e[i].x, y = e[i].y;
if (find(x) != find(y)) {
merge(x, y);
ans += e[i].w;
}
}
cout << ans << '\n';
return 0;
}
分类:
ICPC 训练赛
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】