1. 程式人生 > >[XSY 1549] 冒泡排序

[XSY 1549] 冒泡排序

最大值 pen 若有 冒泡排序 端點 ctype oid inline algorithm

題意

  我們對序列 A 進行如下的冒泡排序.

m = 0;
while (!sorted(A)) {
    m++;
    for (int i = m-1; i >= 1; i--)
        if (A[i] > A[i+1])
            swap(A[i], A[i+1]);
}

  我們定義 m 為對 A 進行冒泡排序的難度.

  求在所有滿足排序難度為 m 的 [1, n] 的排列中, 字典序第 K 小的排列.

  0 <= m <= n-1 < 20 .

  保證 K 合法.

分析

  記 $d_i = \sum_{j}[i < j][a_i > a_j]$ , 即以 i 為左端點的逆序對數.

  我們通過觀察可以發現, 排序難度即為 $\max_i d_i$ .

  現在相當於找逆序對數的最大值為 m 的第 K 個排列.

  我們先研究一下, 逆序對數為 m 的排列個數, 記作 G(n, m) .

  我們考慮差分, 設逆序對數不超過 m 的排列個數為 g(n, m) .

  當 n < m 時, g(n, m) = n! ; 當 n >= m 時, g(n, m) = m! (m+1) ^ {n - m} .

  當 m = 0 時, G(n, m) = g(n, m) ; 當 m > 0 時, G(n, m) = g(n, m) - g(n, m-1) .

  接下來的做法類似數位DP .

  我們考慮從高位到低位, 枚舉每一位的取值, 算個數, 若個數多於 K , 那麽減去, 否則確定.

  我們發現, 之後若有 n 個數, 不用管數值是多少, 直接離散成 1 ~ n 即可.

  需要記錄一個 tag , 表示當前是否取到 m , 註意當前位只能取 1 ~ min(bit, m+1) .

實現

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cctype>
 5 #include <algorithm>
 6
using namespace std; 7 #define F(i, a, b) for (register int i = (a); i <= (b); i++) 8 #define LL long long 9 10 const int N = 25; 11 12 int n, m; LL K; 13 bool v[N]; int List[N]; 14 15 inline LL Pow(int x, int y) { LL mul = 1; F(i, 1, y) mul *= x; return mul; } 16 inline LL Fac(int x) { LL mul = 1; F(i, 1, x) mul *= i; return mul; } 17 inline LL g(int n, int m) { return m >= n ? Fac(n) : Fac(m) * Pow(m+1, n-m); } 18 inline LL G(int n, int m) { return !m ? g(n, m) : g(n, m) - g(n, m-1); } 19 // 0 <= m <= n-1 < 20 20 21 inline void Mark(int bit, int w) { 22 for (int x = 0, cnt = 0; x < n; ) { 23 cnt += !v[++x]; 24 if (cnt == w) { v[x] = true, List[bit] = x; return; } 25 } 26 } 27 28 int main(void) { 29 #ifndef ONLINE_JUDGE 30 freopen("bubble.in", "r", stdin); 31 #endif 32 33 scanf("%d %d %lld", &n, &m, &K); 34 35 bool Gain = false; 36 for (int bit = n; bit >= 1; bit--) { 37 bool done = false; int w; 38 for (w = 1; w < min(m + 1, bit) && !done; w++) { 39 LL cnt = (Gain ? g(bit-1, m) : G(bit-1, m)); 40 if (K <= cnt) done = true; else K -= cnt; 41 } 42 done ? w-- : Gain = true; 43 Mark(bit, w); 44 } 45 46 for (int bit = n; bit >= 1; bit--) 47 printf("%d ", List[bit]); 48 puts(""); 49 50 return 0; 51 }

[XSY 1549] 冒泡排序