1. 程式人生 > >hdu 6318 Swaps and Inversions(歸併排序)

hdu 6318 Swaps and Inversions(歸併排序)

Swaps and Inversions

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1212    Accepted Submission(s): 448

Problem Description

Long long ago, there was an integer sequence a.
Tonyfang think this sequence is messy, so he will count the number of inversions in this sequence. Because he is angry, you will have to pay x yuan for every inversion in the sequence.
You don't want to pay too much, so you can try to play some tricks before he sees this sequence. You can pay y yuan to swap any two adjacent elements.
What is the minimum amount of money you need to spend?
The definition of inversion in this problem is pair (i,j) which 1≤i<j≤n and ai>aj.

Input

There are multiple test cases, please read till the end of input file.
For each test, in the first line, three integers, n,x,y, n represents the length of the sequence.
In the second line, n integers separated by spaces, representing the orginal sequence a.
1≤n,x,y≤100000, numbers in the sequence are in [−109,109]. There're 10 test cases.

Output

For every test case, a single integer representing minimum money to pay.

Sample Input

3 233 666 1 2 3 3 1 666 3 2 1

Sample Output

0 3

題目連結:

題意:這道題的意思是說給你一個數組,你可以花x元或者y元去交換這個陣列中相鄰的兩個數,問變為升序的時候最少花費多少元。

當時聽(隊友講的)這個題的時候,腦子裡第一反應是冒泡(沒用過歸併),肯定超時。最後我們也沒有解決這個題。

作為菜鳥(手動笑哭)我還是先說一下歸併排序吧!

把一個數組分成兩半,先把前一半排序,再把後一半排序,把兩半歸併到一個新的陣列中,然後在拷貝回原來的陣列中。

AC程式碼:

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
using namespace std;
int a[100001],b[100001];
void Merge(int s,int m,int e)
{
    int p=0;
    int p1=s,p2=m+1;//一個指向第一個陣列中的第一個數,一個指向第二個陣列中的第一個數
    while(p1<=m&&p2<=e)
    {
        if(a[p1]<a[p2])//誰小誰在前
            b[p++]=a[p1++];
        else
            b[p++]=a[p2++];
    }
    while(p1<=m)
        b[p++]=a[p1++];
    while(p2<=e)
        b[p++]=a[p2++];
    for(int i=0;i<e-s+1;i++)
        a[s+i]=b[i];
}
void Merge_sort(int s,int e)
{
    if(s<e)
    {
        int m=s+(e-s)/2;
        Merge_sort(s,m);//前一半排序
        Merge_sort(m+1,e);//後一半排序
        Merge(s,m,e);//歸併
    }
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",&a[i]);
    Merge_sort(0,n-1);//0->(n-1)排序
    for(int i=0;i<n-1;i++)
        printf("%d ",a[i]);
    printf("%d\n",a[n-1]);
    return 0;
}
 

下面就來看這道題:會了歸併排序以後這道題就簡單很多了。下面就說怎麼計算交換的次數。

s<=p1<=m,m+1<=p2<=e;

可以看圖,在歸併時,當a[p2]需要新增在陣列中的時候,它必須要和1後面所有的數字交換,次數是:m-s+1;

下面看AC程式碼:

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
using namespace std;
int a[100001],b[100001];
long long times;
void Merge(int s,int m,int e)
{
    int p=0;
    int p1=s,p2=m+1;
    while(p1<=m&&p2<=e)
    {
        if(a[p1]<=a[p2])//因為需要最小次數,所以這裡是<=
            b[p++]=a[p1++];

        else
        {
            b[p++]=a[p2++];
            times+=m-p1+1;
        }
    }
    while(p1<=m)
        b[p++]=a[p1++];
    while(p2<=e)
        b[p++]=a[p2++];
    for(int i=0;i<e-s+1;i++)
        a[s+i]=b[i];
}
void Merge_sort(int s,int e)
{
    if(s<e)
    {
        int m=s+(e-s)/2;
        Merge_sort(s,m);
        Merge_sort(m+1,e);
        Merge(s,m,e);
    }
}
int main()
{
    int n,x,y;
    while(~scanf("%d%d%d",&n,&x,&y))
    {
        for(int i=0;i<n;i++)
        scanf("%d",&a[i]);
        times=0;
        Merge_sort(0,n-1);
        printf("%lld\n",times*min(x,y));//注意資料範圍
    }
    return 0;
}

剛開始因為while(~scanf("%d%d%d",&n,&x,&y))沒有以EOF結束,T了n遍,後來不知道怎麼就看出來了,然後就是<=那個地方和資料範圍那裡。一下午就做了兩個題,一個是第一場貪心那個題,WA了n次,決定學了資料結構之後再去做,另外一個就是這個,在超時,超記憶體n次之後終於AC了。還是做得題太少了,就算當時會了歸併,我覺得我有可能還是做不出來。找不到題的坑點(流淚)。