D. Binary String Sorting
D. Binary String Sorting
You are given a binary string consisting of only characters 0 and/or 1.
You can perform several operations on this string (possibly zero). There are two types of operations:
- choose two consecutive elements and swap them. In order to perform this operation, you pay coins;
- choose any element from the string and remove it. In order to perform this operation, you pay coins.
Your task is to calculate the minimum number of coins required to sort the string in non-decreasing order (i. e. transform so that , where is the length of the string after applying all operations). An empty string is also considered sorted in non-decreasing order.
Input
The first line contains a single integer () — the number of test cases.
The only line of each test case contains the string (), consisting of only characters 0 and/or 1.
The sum of lengths of all given strings doesn't exceed .
Output
For each test case, print a single integer — the minimum number of coins required to sort the string in non-decreasing order.
Example
input
6 100 0 0101 00101101 1001101 11111
output
1000000000001 0 1000000000000 2000000000001 2000000000002 0
Note
In the first example, you have to remove the -st element, so the string becomes equal to 00.
In the second example, the string is already sorted.
In the third example, you have to swap the -nd and the -rd elements, so the string becomes equal to 0011.
In the fourth example, you have to swap the -rd and the -th elements, so the string becomes equal to 00011101, and then remove the -th element, so the string becomes equal to 0001111.
In the fifth example, you have to remove the -st element, so the string becomes equal to 001101, and then remove the -th element, so the string becomes equal to 00111.
In the sixth example, the string is already sorted.
解题思路
先给出dp的做法。
定义状态
- 表示将前个数字处理成非递减序列,且最后一个数字是的所有操作方案的集合。
- 表示将前个数字处理成非递减序列,且最后恰好有一个的所有操作方案的集合。
- 表示将前个数字处理成非递减序列,且最后至少有两个连续的的所有操作方案的集合。
三种状态的属性均是是操作代价的最小值。
每个状态根据前一次操作后的状态来转移得到。定义。
- 如果,
- 对于,可以直接从转移过来,代价就是。也可以从状态和删除最后所有的转移到,最后再接上转移过来,代价至少为。
- 对于对于,无法从状态转移过来。可以删除然后直接从状态转移过来,代价是,或者与状态最后一个交换转移过来,代价是,很明显后者更小。也可以从状态删除若干个得到,然后再删除转移过来,代价至少为。
- 对于对于,无法从状态和转移过来。可以删除然后直接从状态转移过来,代价就是。
- 如果,
- 对于,可以删除然后直接从状态转移过来,代价是。也可以从状态和删除最后所有的转移到,然后再删除转移过来,代价至少为。
- 对于对于,可以直接从转移过来,代价就是。也可以从状态和删除最后若干个转移到,然后再删除转移过来,代价至少为。
- 对于对于,无法从状态转移过来。可以直接从状态和转移过来,代价就是。
综上所述,状态转移方程为:
AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 6 const int N = 3e5 + 10; 7 const LL M = 1e12; 8 9 char s[N]; 10 LL f[N][3]; 11 12 void solve() { 13 scanf("%s", s + 1); 14 int n = strlen(s + 1); 15 for (int i = 1; i <= n; i++) { 16 if (s[i] == '0') { 17 f[i][0] = f[i - 1][0]; 18 f[i][1] = f[i - 1][1] + M; 19 f[i][2] = f[i - 1][2] + M + 1; 20 } 21 else { 22 f[i][0] = f[i - 1][0] + M + 1; 23 f[i][1] = f[i - 1][0]; 24 f[i][2] = min(f[i - 1][1], f[i - 1][2]); 25 } 26 } 27 printf("%lld\n", min({f[n][0], f[n][1], f[n][2]})); 28 } 29 30 int main() { 31 int t; 32 scanf("%d", &t); 33 while (t--) { 34 solve(); 35 } 36 37 return 0; 38 }
还有一种贪心的做法。
可以发现在由和构成的序列中,如果逆序对恰好为,那么我们只需要执行一次交换操作就可以了。否则逆序对的数量超过个,意味着至少存在一个数需要至少交换次,因此直接删除这个数最优。这意味着我们最多只执行一次交换操作,其余的都执行删除操作。
枚举分界点将序列分成前部分和后部分,要将变成非递减序列,首先很容易想到把前部分中所有的删除,把后部分中所有的删除。而只有当时我们才交换和,此时代价就是,而删除这两个元素的代价是,明显交换会更优。而其他位置的交换呢?这时对于前部分在位置的元素,要将位置上的元素交换到后部分的位置至少需要,很明显不如直接删除来的更优。同理对于后部分在位置的元素,直接删除会更优。
因此直接预处理前缀中的个数和后缀中的个数,当枚举到分界点,此时变成非递减序列的最小代价就是
注意只有才能交换,即才有。
最后还要考虑前部分为而后部分为空,和后部分为而前部分为空的情况,这里的处理方法可以看代码实现。
AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 6 const int N = 3e5 + 10; 7 const LL M = 1e12; 8 9 char s[N]; 10 int f[N], g[N]; 11 12 void solve() { 13 scanf("%s", s + 1); 14 int n = strlen(s + 1); 15 f[0] = g[n + 1] = 0; 16 for (int i = 1; i <= n; i++) { 17 f[i] = f[i - 1]; 18 if (s[i] == '1') f[i]++; 19 } 20 for (int i = n; i; i--) { 21 g[i] = g[i + 1]; 22 if (s[i] == '0') g[i]++; 23 } 24 LL ret = 1e18; 25 for (int i = 0; i <= n; i++) { 26 ret = min(ret, (f[i] + g[i + 1]) * (M + 1)); 27 if (s[i] == '1' && s[i + 1] == '0') ret = min(ret, (f[i] + g[i + 1] - 2) * (M + 1) + M); 28 } 29 printf("%lld\n", ret); 30 } 31 32 int main() { 33 int t; 34 scanf("%d", &t); 35 while (t--) { 36 solve(); 37 } 38 39 return 0; 40 }
参考资料
Educational Codeforces Round 145 (Rated for Div. 2) A - D:https://zhuanlan.zhihu.com/p/616545904
Educational Codeforces Round 145 Editorial:https://codeforces.com/blog/entry/114300
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17250432.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2022-03-24 砝码称重
2022-03-24 社交距离 I