计蒜客_三值排序

问题描述 :

排序是一种很频繁的计算任务。一个实际的例子是,当我们给某项竞赛的优胜者按金银铜牌排序的时候。在这个任务中可能的值只有三种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();
}
如有不当,欢迎指正 :)
原文地址:https://www.cnblogs.com/lif323/p/7389636.html