[题解] [CCPC陕西省赛2022 C题 ] Type The String

wxy3265·2024-04-15 11:44·30 次阅读

[题解] [CCPC陕西省赛2022 C题 ] Type The String

[CCPC陕西省赛2022 C题 ] Type The String

题目描述#

给定 \(n\) 个字符串,以任意顺序打到文本编辑器里,每个字符串一行,打字的方法有两种:

  1. 挨个字母打,每个字母的代价是 \(1\)
  2. 先把已经打的一行复制到现在这一行,然后在任意位置增删任意个字符,复制的代价是给定的固定值 \(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代码#

Copy
#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; }
posted @   wxy3265  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
目录