[英]Can someone please explain this approach to me?
我正在通过 USACO 培训网站工作。 我发现这个问题要求我们在三值序列中找到最小的交换次数。 虽然我已经用 O(n^2) 解决方案解决了这个问题,但我很想找出这种更快的 O(n) 方法。 但是,作为初学者,我发现它相当难以理解。
这是详细的问题陈述和最佳方法。
在此任务中,可能的键值是整数 1、2 和 3。所需的排序顺序是非递减的。 但是,排序必须通过一系列交换操作来完成。 由两个 position 编号 p 和 q 定义的交换操作交换位置 p 和 q 中的元素。
您将获得一系列键值。 编写一个程序,计算使序列排序所需的最少交换操作数。
#include <fstream>
using namespace std;
int min (int a, int b) { return a < b ? a : b; }
int max (int a, int b) { return a > b ? a : b; }
int main () {
int s[1024];
int n;
int sc[4] = {0};
ifstream fin("sort3.in");
ofstream fout("sort3.out");
fin>>n;
for (int i = 0; i < n; i++) {
fin>>s[i];
sc[s[i]]++;
}
int s12 = 0, s13 = 0, s21 = 0, s31 = 0, s23 = 0, s32 = 0;
for (int i = 0; i < sc[1]; i++){
if (s[i] == 2) s12++;
if (s[i] == 3) s13++;
}
for (int i = sc[1]; i < sc[1]+sc[2]; i++){
if (s[i] == 1) s21++;
if (s[i] == 3) s23++;
}
for (int i = sc[1]+sc[2]; i < sc[1]+sc[2]+sc[3]; i++){
if (s[i] == 1) s31++;
if (s[i] == 2) s32++;
}
fout<<min(s12, s21)+min(s13, s31)+min(s23, s32) +
2*(max(s12, s21) - min(s12, s21))<<endl;
return 0;
}
我得到了我们在各自所需范围 [0->c1, c1->c1+c2, c1+c2->n] 中取最小重叠的部分。 但是,我不太明白 2*max(s12, s21) - min(s12, s21) 的公式。 还有为什么我们不在公式中考虑 s13、s31、s23 和 s32。 我希望您向我解释这一点,因为我不打算在我的训练中留下任何未弥补的空白。 提前致谢!
如果我们从另一个角度处理问题并在示例中计算出如何执行交换,我们可以看到交换次数的公式来自何处。
如果我们开始:
22121 23331 1231
s12
为3
s13
为0
s21
为1
s23
是3
s31
是2
s32
为1
然后用1
交换( min(s12, s21)
)交换1
和2
给出:
12121 23332 1231
1
和3
之间没有交换( min(s13, s31)
)。
然后用1
交换( min(s23, s32)
)交换2
和3
给出:
12121 22332 1331
现在我们没有更多的直接交换了,我们需要交换两次才能将剩余的元素放到正确的位置。 错位有 2 个1
,错位有2
个,错位有 2 个3
,每个数字的数字必须始终相同。 错位的1
的初始数量由max(s12, s21)
给出,我们已经修复了那些留下max(s12, s21) - min(s12, s21)
min(s12, s21)
待修复。 我们需要执行 2 次交换来修复每个不正确的元素。
首先将1
s 应该在的2
s 与3
s 应该在的1
s 交换,这需要2
交换,给出:
11111 22332 2332
现在我们可以直接交换2
s 和3
s,再进行2
交换,得到最终结果:
11111 22222 3333
将掉期总数相加得出:
min(s12, s21) + min(s13, s31) + min(s23, s32) + 2*(max(s12, s21) - min(s12, s21))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.