整理书籍
整理书籍
书架上有若干本书排成一排。
每本书要么是大型书(用 L 表示),要么是中型书(用 M 表示),要么是小型书(用 S 表示)。
我们希望所有书能够从大到小有序排列,也就是说,所有大型书都在左侧,所有中型书都在中间,所有小型书都在右侧。
为此,你可以进行任意次交换操作,每次可以任选两本书并交换它们的位置。
请你计算,为了让所有书按要求有序排列,至少需要进行多少次交换操作。
输入格式
共一行,包含一个由 L、M、S 构成的字符串,表示初始时每个位置上的书的类型。
输出格式
一个整数,表示所需要的最少交换操作次数。
数据范围
输入字符串的长度范围 。
输入样例1:
LMMMS
输出样例1:
0
样例1解释
无需任何操作,初始排列已经符合要求。
输入样例2:
LLSLM
输出样例2:
2
样例2解释
一种最佳方案如下:
- 第一步,交换 S 和 M,序列变为 LLMLS。
- 第二步,交换 M 和它右边的 L,序列变为 LLLMS 。
解题思路
看到选择任意两个位置交换来使得序列有序,很容易想到全排列的置换群。不过要用全排列的置换群的做法的话要保证没用重复元素出现,很明显这题不适用。不过实际上还是会用到环图,做法也是很类似的。
令,,分别对应,,。假设原始序列是,排序后的序列是,依次遍历每一个元素,让向连一条边,就会得到一个环图。例如有,对应的就有,得到的图如下:
可以发现构造图的方式与构造全排列置换群是一样的,建边方式都是。不过这个问题中不同种类的元素最多只有种,因此图中最多有个节点(可以类比,当不同种类的元素最多有种,那么图中最多有个节点)。同时每个节点的入度与出度都是一样的,因为同一个值在和中出现的次数都一样,因此这个图也是一个欧拉图,而欧拉图也是由一堆环构成的。
我们最终要将序列变成,意味着要将图变成个自环,与全排列的置换群一样,每交换两个元素最多让一个环裂变成两个环。假设当前有个环,由于最终要变成个自环,因此至少要交换次。可以发现要使得尽可能小,就要让尽可能的大,即对于构造出的原图中要找到尽可能多的环。
可以发现在这个问题中环的长度只有三种,即长度为的自环,长度为的环,以及长度为的大环。为此我们应该从长度最小的环开始找起,因为环的长度越小则用到的边也越少,剩的边越多得到的环可能会更多。
定义的矩阵,其中表示从节点指向的边的数量(即图的邻接矩阵表示)。那么长度为的环的数量就是,能得到长度为的环的最大数量是。然后在图中删除已选出来的边,由于删边后还是保证图中每个节点的入度和出度相同,因此图中就只剩下长度为的大环,且任意两点间边的数量都相同,所以长度为的环的数量就是(可能是逆时针的大环,或是顺时针的大环,不可能同时出现否则会存在长度为的环)。
AC代码如下,时间复杂度为:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 5e5 + 10; 5 6 char s[N]; 7 int a[N], b[N]; 8 int e[3][3]; 9 10 int main() { 11 scanf("%s", s); 12 int n = strlen(s); 13 for (int i = 0; i < n; i++) { 14 int t = 0; 15 if (s[i] == 'M') t = 1; 16 else if (s[i] == 'S') t = 2; 17 a[i] = b[i] = t; 18 } 19 sort(b, b + n); 20 for (int i = 0; i < n; i++) { 21 e[a[i]][b[i]]++; 22 } 23 int ret = 0; 24 for (int i = 0; i < 3; i++) { 25 ret += e[i][i]; 26 for (int j = i + 1; j < 3; j++) { 27 int t = min(e[i][j], e[j][i]); 28 ret += t; 29 e[i][j] -= t, e[j][i] -= t; 30 } 31 } 32 ret += max(e[0][1], e[1][0]); 33 printf("%d", n - ret); 34 35 return 0; 36 }
可以开个哈希表用计数排序优化,时间复杂度就降到了。AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 5e5 + 10; 5 6 char s[N]; 7 int a[N], b[N]; 8 int e[3][3], cnt[3]; 9 10 int main() { 11 scanf("%s", s); 12 int n = strlen(s); 13 for (int i = 0; i < n; i++) { 14 int t = 0; 15 if (s[i] == 'M') t = 1; 16 else if (s[i] == 'S') t = 2; 17 a[i] = t; 18 cnt[t]++; 19 } 20 for (int i = 0, k = 0; i < 3; i++) { 21 for (int j = 0; j < cnt[i]; j++){ 22 b[k++] = i; 23 } 24 } 25 for (int i = 0; i < n; i++) { 26 e[a[i]][b[i]]++; 27 } 28 int ret = 0; 29 for (int i = 0; i < 3; i++) { 30 ret += e[i][i]; 31 for (int j = i + 1; j < 3; j++) { 32 int t = min(e[i][j], e[j][i]); 33 ret += t; 34 e[i][j] -= t, e[j][i] -= t; 35 } 36 } 37 ret += max(e[0][1], e[1][0]); 38 printf("%d", n - ret); 39 40 return 0; 41 }
参考资料
AcWing 5198. 整理书籍(秋季每日一题2023):https://www.acwing.com/video/4931/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17681964.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2022-09-06 Count Unique Characters of All Substrings of a Given String