1. 程式人生 > >USACO-Section2.1 Sorting a Three-Valued Sequence [其他][排序][交換]

USACO-Section2.1 Sorting a Three-Valued Sequence [其他][排序][交換]

題目大意

排序是一種很頻繁的計算任務。現在考慮最多隻有三值的排序問題。一個實際的例子是,當我們給某項競賽的優勝者按金銀銅牌排序的時候。在這個任務中可能的值只有三種1,2和3。我們用交換的方法把他排成升序的。
寫一個程式計算出,給定的一個1,2,3組成的數字序列,排成升序所需的最少交換次數。

題解

NOCOW上給出過一種思路:
題意求排序所需的最少移動次數,可以先將輸入的數字排序,然後得到不同的地方
比如:
2 2 1 3 3 3 2 3 1
1 1 2 2 2 3 3 3 3
用一個組合(a,b)表示應該排序後某個位置應該是a,但之前的是b
則我們得到(1,2), (1,2), (2,1), (2,3), (2,3), (3,2), (3,1)
然後求這些組合能組成的環,結果就是所有環的長度和減去環的個數
(1,2), (2,1) —長度為2
(1,2), (2,3), (3,1) —長度為3
(2,3), (3,2) —長度為2
結果2+3+2-3=4

這個思路是沒錯的,但有一點需要注意,演算法必須保證先消去小環,再消去大環。比如上例中完全可以出現(1,2),(2,3),(3,2),(2,3),(3,1)這樣的環。多個小環的代價要小於一個大環。
上例中 (1,2),(2,3),(3,2),(2,3),(3,1) 是由 (1,2),(2,3)(3,1) 和(3,2), (2,3)組合成的。也就是說,一個5元的環,有可能是一個二元環和一個三元環組成的。一個真五元環需要交換4次,真3元環需要交換2次,一個2元環只需要交換1次,所以把它看成一個大環和多個小環是不一樣的。而且多個小環的代價要小於一個大環。
長度為n的環,

/=(n1)/n
所以n越小,其比值越小。所以我們需要先把小環中的數字交換,再處理更大的環。
那麼怎麼才能保證先消去小環,在消大環呢

對於這一題,只有三種元素,1、2、3,所以所有可能的環的情況只有4種:
1. (1,2), (2,1)
2. (2,3), (3,2)
3. (1,3), (3,1)
4. (1,2), (2,3), (3,1) 或 (1,3), (3,2), (2,1)

我們只需要先把所有的二元環都處理完再處理三元的環就可以了。
把整個陣列分成3部分,這3部分分別是排好序後1,2,3的位置。
令c[i][j] = 在第i個位置上的數字j的個數。例如
原陣列:2 2 | 1 3 3 | 3 2 3 1
排序後:1 1 | 2 2 2 | 3 3 3 3
可以看出 c[1][1] = 0, c[1][2] = 2,c[1][3] = 0,…
然後就可以按照上面的方法從小到大尋找環了。

程式碼

/*
ID: zachery1 
PROG: sort3
LANG: C++
*/
#include<iostream>
#include<fstream>
#include<cstring>
#define N 1001
#define cout fout
#define cin fin
using namespace std;
ifstream fin("sort3.in");
ofstream fout("sort3.out");
int main()
{
    int n;
    int ans=0;
    int a[N]={0};
    int b[4]={0};
    int c[4][4]={0};
    cin >> n;
    for(int i=0; i<n; i++)
    {
        cin >> a[i];
        b[a[i]]++;
    } 
    int k1, k2;
    k1 = b[1]; k2 = b[1]+b[2];
    for(int i=0; i<k1; i++) c[1][a[i]]++;
    for(int i=k1; i<k2; i++) c[2][a[i]]++;
    for(int i=k2; i<n; i++) c[3][a[i]]++;

    while(c[1][2] && c[2][1])
    {
        c[1][2]--; c[2][1]--;
        ans++;
    }
    while(c[1][3] && c[3][1])
    {
        c[1][3]--; c[3][1]--;
        ans++;
    }
    while(c[2][3] && c[3][2])
    {
        c[2][3]--; c[3][2]--;
        ans++;  
    } 
    while(c[1][2] && c[2][3] && c[3][1])
    {
        c[1][2]--; c[2][3]--; c[3][1]--;
        ans+=2;
    }
    while(c[1][3] && c[3][2] && c[2][1])
    {
        c[1][3]--; c[3][2]--; c[2][1]--;
        ans+=2;
    }
    cout << ans << endl;
    return 0;
}