• uva 10810


    题意为,给你一个序列, 每次交换两个相邻的数使序列为递增的序列, 求最小的交换次数。

    首先我们可以看出。 最少的交换次数肯定得用归并排序来求了。

    实际上归并排序的交换次数就是这个数组的逆序对个数,为什么呢?

    我们可以这样考虑:

    归并排序是将数列a[l,h]分成两半a[l,mid]和a[mid+1,h]分别进行归并排序,然后再将这两半合并起来。

    在合并的过程中(设l<=i<=mid,mid+1<=j<=h),当a[i]<=a[j]时,并不产生逆序数;当a[i]>a[j]时,在

    前半部分中比a[i]大的数都比a[j]大,将a[j]放在a[i]前面的话,逆序数要加上mid+1-i。因此,可以在归并

    排序中的合并过程中计算逆序数.

     1 #include <cstdio>
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <cstring>
     5 #include <cmath>
     6 #include <cstdlib>
     7 #include <string>
     8 #include <map>
     9 #include <vector>
    10 #include <set>
    11 #include <queue>
    12 #include <stack>
    13 #include <cctype>
    14 using namespace std;
    15 typedef long long LL;
    16 typedef unsigned long long ULL;
    17 #define MAXN 500000+10
    18 #define INF (1<<30)
    19 #define mod 123456789
    20 int a[MAXN];
    21 int tem[MAXN];
    22 LL total = 0;
    23 int Merge_sort(int l, int r){
    24     int mid = (l+r)>>1;
    25     int x = l, y = mid+1;
    26     int ans = 0;
    27     while(x <= mid && y <= r){
    28         if(a[x] > a[y]){
    29             tem[ans++] = a[y];
    30             y++;
    31             total += mid-x+1;
    32         }
    33         else {
    34             tem[ans++] = a[x];
    35             x++;
    36         }
    37     }
    38     while(x <= mid) tem[ans++] = a[x++];
    39     while(y <= r)   tem[ans++] = a[y++];
    40     ans = 0;
    41     for(int i = l; i <= r; i++)
    42         a[i] = tem[ans++];
    43 }
    44 int Merge(int l,int r){
    45     if(l < r){
    46         int mid = (l+r)>>1;
    47         Merge(l,mid);
    48         Merge(mid+1,r);
    49         Merge_sort(l,r);
    50     }
    51 }
    52 int main (){
    53     int n;
    54     while(scanf("%d",&n) != EOF && n){
    55         total = 0;
    56         for(int i = 0; i < n; i++){
    57             scanf("%d",&a[i]);
    58         }
    59         Merge(0,n-1);
    60         cout << total << endl;
    61     }
    62     return 0;
    63 }

    来源

    目前求逆序对数目比较普遍的方法是利用归并排序做到O(n log n)的时间复杂度。 当然,也可以利用树状数组、线段树来实现这种基础功能。复杂度均为O(n log n)

     1 #include <stdio.h>
     2 #define inf 2000000000
     3 #define sz 500005
     4 
     5 void mergesort(int p, int r);
     6 void merge(int p, int q, int r);
     7 int a[sz], L[(sz/2)+5], R[(sz/2)+5];
     8 long long int cnt;
     9 
    10 int main()
    11 {
    12     int n, i;
    13     while(scanf("%d", &n) && n)
    14     {
    15         for(i = 1; i <= n; i++)
    16             scanf("%d", &a[i]);
    17         cnt = 0;
    18         mergesort(1, n);
    19         printf("%lld
    ", cnt);
    20     }
    21     return 0;
    22 }
    23 
    24 void mergesort(int p, int r)
    25 {
    26     if(p < r)
    27     {
    28         int q;
    29         q = (p+r)/2;
    30         mergesort(p, q);
    31         mergesort(q+1, r);
    32         merge(p, q, r);
    33     }
    34     return ;
    35 }
    36 
    37 void merge(int p, int q, int r)
    38 {
    39     int ind1, ind2, k, i, j;
    40     for(i = p, ind1 = 1; i <= q; i++)
    41         L[ind1++] = a[i];
    42     L[ind1] = inf;
    43     for(i = q + 1, ind2 = 1; i <= r; i++)
    44         R[ind2++] = a[i];
    45     R[ind2] = inf;
    46     i = j = 1;
    47     for(k = p; k <= r; k++)
    48     {
    49         if(L[i] > R[j])
    50         {
    51             cnt += ind1 - i;
    52             a[k] = R[j];
    53             j++;
    54         }
    55         else
    56         {
    57             a[k] = L[i];
    58             i++;
    59         }
    60     }
    61     return ;
    62 }
     1 /*
     2  *  Problem : "Ultra-QuickSort"
     3  *  ID      : 10810
     4  *  Date    : 2011-03-15
     5  *  Idea    : 這題可以學到很多東西,雖然題目是叫 Quick Sort ,但是它卻要求我們要記錄 swap 的次數,
     6  *            因此第一時間想到的就是 10327 題的 Flip Sort (即 Bubble Sort),不過這題是其進階版,
     7  *            最大的問題就是要克服 TLE 的問題( O(n^2) 一定會超過 3 秒),所以我們從 Merge Sort 下手
     8  *            然後發現 Merge Sort 其 Left Array & Right Array 之間其實存在一個很奇妙的關係,要記錄
     9  *            Swap 次數是要在 L > R 的時候才需要,而且公式就是 n1 - i + 1(可在紙上演練一下)。
    10  *
    11  *            另外要注意一下就是 count 可能 overflow ,記得 long long !
    12  *  Author  : EragonJ
    13  */
    14 #include <iostream>
    15 #define MAX 502000
    16 using namespace std;
    17 
    18 long long int count = 0;
    19 
    20 void merge(int A[], int p, int q, int r) {
    21   extern long long int count;
    22 
    23   int n1 = q - p + 1;
    24   int n2 = r - q;
    25 
    26   int* L = (int*) malloc((n1 + 1) * sizeof(int));
    27   int* R = (int*) malloc((n2 + 1) * sizeof(int));
    28 
    29   for (int i = 1; i <= n1; i++) {
    30     L[i] = A[p + i - 1];
    31   }
    32 
    33   for (int i = 1; i <= n2; i++) {
    34     R[i] = A[q + i];
    35   }
    36 
    37   int i, j, k;
    38   for (k = p, i = 1, j = 1; (k <= r) && (i <= n1) && (j <= n2); k++) {
    39     if (L[i] <= R[j]) {
    40       A[k] = L[i];
    41       i++;
    42     }
    43     else {
    44       A[k] = R[j];
    45       j++;
    46       count += (n1 - i + 1); // Important formula
    47     }
    48   }
    49 
    50   for (i; i <= n1; i++) {
    51     A[k++] = L[i]; 
    52   }
    53 
    54   for (j; j <= n2; j++) {
    55     A[k++] = R[j];
    56   }
    57 }
    58 
    59 void merge_sort(int A[], int p, int r) {
    60   if (p < r) {
    61     int q = (p + r) / 2;
    62     merge_sort(A, p, q); 
    63     merge_sort(A, q + 1, r);
    64     merge(A, p, q, r);
    65   }
    66 }
    67 
    68 int main() {
    69 
    70   int n;
    71   int input[MAX] = {0};
    72   while (scanf("%d", &n) == 1) {
    73     if (n == 0) {
    74       break;
    75     }
    76 
    77     for (int i = 0; i < n; i++) {
    78       // Index range : 1 ~ n;
    79       scanf("%d", &input[i + 1]);
    80     }
    81 
    82     merge_sort(input, 1, n);
    83 
    84     extern long long int count;
    85     cout << count << endl;
    86 
    87     count = 0;
    88   }
    89 
    90   return 0;
    91 }

    解题方法:离散化+树状数组

     1 /*题目大意:给出一个序列,每次交换两个数,这两个数之间的距离就是代价,问说要将序列排序的总代价是多少。
     2 解题思路:归并排序下的逆序数的个数。*/
     3 #include <stdio.h>
     4 #include <string.h>
     5 
     6 const int N = 500005;
     7 typedef long long ll;
     8 int n, g[N], f[N];
     9 
    10 ll Msort(int l, int r, int* a, int* b) {
    11     if (r - l == 1) return 0;
    12 
    13     int m = (l + r) / 2;
    14     ll ans = Msort(l, m, a, b) + Msort(m, r, a, b);
    15     int p = l, q = m, c = l;
    16     while (p < m || q < r) {
    17         if (q >= r || (p < m &&  a[p] <= a[q])) b[c++] = a[p++];
    18         else {
    19             ans += m - p;
    20             b[c++] = a[q++];
    21         }
    22     }
    23     for (int i = l; i < r; i++) a[i] = b[i];
    24     return ans;
    25 }
    26 
    27 int main() {
    28     while (scanf("%d", &n) == 1 && n) {
    29         for (int i = 0; i < n; i++) scanf("%d", &g[i]);
    30         printf("%lld
    ", Msort(0, n, g, f));
    31     }
    32     return 0;
    33 }
  • 相关阅读:
    软件开发规范
    内置模块
    自定义模块
    装饰器 递归
    内置函数 闭包
    生成器 推导式
    函数名运用 新版格式化输出 迭代器
    函数进阶
    pycharm快捷键
    移动端必测点
  • 原文地址:https://www.cnblogs.com/aze-003/p/5158333.html
Copyright © 2020-2023  润新知