zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    js截取字符串区分汉字字母代码
    List 去处自定义重复对象方法
    63. Unique Paths II
    62. Unique Paths
    388. Longest Absolute File Path
    41. First Missing Positive
    140. Word Break II
    139. Word Break
    239. Sliding Window Maximum
    5. Longest Palindromic Substring
  • 原文地址:https://www.cnblogs.com/aze-003/p/5158333.html
Copyright © 2011-2022 走看看