poj2299

poj2299題目鏈接
題意:
一個含有n個數的數組, 每次只能交換相鄰的兩個數, 求最少操作多少次可以使該數組變成一個有序數組(從小到大)。
分析:
先說一下歸併排序吧。 二分的思想, 就是將一元素集合分割成兩個或更多個子集合,對每一個子集合分別排序,然後將排好序的子集合歸併爲一個集合。看圖理解會好一點!
這裏寫圖片描述

歸併排序核心操作:將一維數組中前後相鄰的兩個有序序列歸併爲一個有序序列。

那看一下我們這題, 其實就是在歸併排序的過程中順便計算一下移動次數, 就好了。 舉個例子:例如圖中前半部分數組{8,3,2,9}, 先分爲兩部分{8,3} 和{2,9} 。 {8,3}又分爲{8} 和{3}, 8又在3前面所以合併8,3需要移動一次 sum= 1, {2,9}分爲{2} he {9}, 本來就是有序的所以合併2,9不需要移動 sum= 1; 接下來這一步就該合併{3,8} 和{2,9}了。 2前面有兩個比它大的數,所以要交換兩次, 2才能排到第一位 sum = sum+2 = 3; 9比前面的都大就不用交換啦 sum= 3。


#include<iostream>
#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;

const int N = 500005;
__int64 sum, a[N], c[N];
int n;
void merge1(int low, int high, int mid)
{
    int i = low, j = mid + 1;
    int k = 0;
    while((i <=  mid) && (j <= high))
    {
        if(a[i] <= a[j])
        {
            c[++k] = a[i];
            i++;
        }
        else if(a[i] > a[j])
        {
            c[++k] = a[j];
            j++;
            sum += (mid - i + 1);
        }
    }
    while(i <= mid)
        c[++k] = a[i++];
    while(j <= high)
        c[++k] = a[j++];
    if(low < high)
    {
        for(int i = high; i >= low; i--)
        {
            a[i] = c[k];
            k--;
        }
    }
}
//通過ac()不斷將數組劃分爲更小的區間,再通過merge1()將劃分的數組再合並回來, 並且合併的時候使其變得有序
void ac(int low, int high)
{
    if(low < high)
    {
        int mid = (low + high) / 2;
        ac(low, mid);
        ac(mid + 1, high);
        merge1(low, high, mid);
    }
}
int main()
{
    while(scanf("%d", &n) != EOF && n)
    {
        for(int i = 1; i <= n; i++)
            scanf("%I64d", &a[i]);
        sum = 0;
        ac(1, n);
        printf("%I64d\n", sum);
    }
    return 0;
}
發佈了32 篇原創文章 · 獲贊 2 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章