问题描述 :
排序是一种很频繁的计算任务。一个实际的例子是,当我们给某项竞赛的优胜者按金银铜牌排序的时候。在这个任务中可能的值只有三种1,2和3。我们用交换的方法把他排成升序的。
写一个程序计算出,计算出的一个包括1、2、3三种值的数字序列,排成升序所需的最少交换次数。
输入第1行为类别的数量N(1≤N≤1000)
输入第2行到第N+1行,每行包括一个数字(1或2或3)。
输出包含一行,为排成升序所需的最少交换次数。
样例输入
9
2
2
1
3
3
3
2
3
1
样例输出
4
解题思路:
起初,看到这样的问题,感到无从下手,不过把样例的解题过程搞明白,思路基本就出来了。
首先,把没有在正确位置的元素表示出来,
2 1
2 1
1 2
3 2
3 2
3 3
2 3
3 3
1 3
样例原始输入中错误的元素个数为7,正顺序的错误元素个数为0,目标就是通过交换,不断减少错误元素的个数,知道减为0;
容易知道,如果想达到正确顺序,必须是错误元素和错误元素相互交换,选择恰当时,可减少2个(1放在了2的元素和2放在了1上的元素,例如样例输入中的第二个元素和第三个元素),
或者1个(1放在了3的元素和2放在了1的元素交换,例如样例中的第一个元素和第5个元素)
代码如下,主要是两个for循环,时间复杂度为O(n).
#include<iostream>
using namespace std;
int getNum(int*a, int len) {
int num[4] = {0};
//前两个for循环和计数排序过程相近,第一个for循环记录等于1,2和3对应的元素个数,
//第二个for循环记录通过累加的方式记录小于等于某个元素的总个数,例如
//第二个for循环以后,如果num[2]=4,也就是说num[1](等于1的个数)+num[2](等于2的个数)=num[2];
//则(排序以后)值为2的元素下标index满足num[1]<=index<num[2];
for (int i = 0; i < len; ++i) {
++num[a[i]];
}
for (int i = 1; i < 4; ++i) {
num[i] += num[i - 1];
}
//x31意思是3放在1的位置的个数,x21为2放在1的位置的个数,以下的变量意义相同。
//以下三个for循环记录相应错误元素的个数。
int x31 = 0, x21 = 0;
for (int i = 0; i < num[1]; ++i) {
if (a[i] == 2) {
++x21;
}
else if (a[i] == 3) {
++x31;
}
}
int x12 = 0, x32 = 0;
for (int i = num[1]; i < num[2]; ++i) {
if (a[i] == 1) {
++x12;
}
else if (a[i] == 3) {
++x32;
}
}
int x13 = 0, x23 = 0;
for (int i = num[2]; i < num[3]; ++i) {
if (a[i] == 1) {
++x13;
}
else if (a[i] == 2) {
++x23;
}
}
//首先,将x12类型和x21类型(对应的还有x31和x13,x23和x32)相互交换,交换次数为两者较小值,每次交换可以减少两个错误元素的个数。
int counts = 0;
if (x12 > x21) {
counts += x21;
x12 -= x21;
x21 = 0;
}
else {
counts += x12;
x21 -= x12;
x12 = 0;
}
if (x13 > x31) {
counts += x31;
x13 -= x31;
x31 = 0;
}
else {
counts += x13;
x31 -= x13;
x13 = 0;
}
if (x23 > x32) {
counts += x32;
x23 -= x32;
x32 = 0;
}
else {
counts += x23;
x32 -= x23;
x23 = 0;
}
//通过推理可知,x13,x32,x21如果三者中有一个不等于零,则三者必然相等,(先将x13和x21交换,剩余x23和x32,两者再次交换,总次数为2*x13)
//同理还有(x31,x12,x23);
if (x13 != 0) {
counts += 2 * x13;
}
else if (x31 != 0) {
counts += 2 * x31;
}
return counts;
}
void input() {
int n;
cin >> n;
int a[1000];
for (int i = 0; i < n; ++i)
cin >> a[i];
cout << getNum(a, n) << endl;
}
int main() {
input();
}