HDU 1394 暴力 或 歸併排序 或 線段樹

2021-08-08 19:47:09 字數 2460 閱讀 3598

題意:給定n個數,乙個合法操作是每次可以將數列的第乙個元素放到數列的尾部,然後問你所有可能操作中的逆序對的個數最少是多少。乙個逆序對(ai,aj)的定義是數列a中下標 i 小於 j 同時 ai 大於 aj。

思路:看到資料範圍只有5000,一開始就往暴力方面想了。然後模擬一下發現了乙個比簡單的規律。對於乙個n個數的數列a,b,c,d……..,每個數都是0到n-1,設當前的逆序對個數是cnt,那麼一次操作後逆序對個數會變成cnt+n-2*a-1個,其實蠻顯然的,因為數列中比a大的數有n-1-a個,比a小的數有a個。o(n*n)計算出事的逆序對個數,然後在o(n)遍歷一邊統計最小值就可以了。

暴力

#include

using

namespace

std;

const

int inf = 0x3f3f3f3f;

int main()

int cnt=0;

for(int i=0;ifor(int j=i+1;jif(a[j]int mi=inf;

for(int i=0;i1-(2*a[i]));

mi=min(cnt,mi);

}printf("%d\n",mi);

}return

0;}

這題的資料範圍很小,在大資料的情況暴力可能會t。想了一下優化,有兩個思路,乙個是歸併排序優化,乙個是線段樹優化。

歸併排序

考慮這樣乙個情況,把n個數的序列a分為一般長度的兩個序列l和r,那麼a的逆序對個數就等於l、r中自身的逆序對個數之和再加上跨越l和r的逆序對個數。這不正好是歸併排序的思想麼。r和l中的逆序對個數迭代到1的時候很容易計算,那麼如何快速計算跨越l和r的逆序對re呢。基於這樣乙個事實,我們分別把l、r中的資料進行排序是不會影響re的值的。排序後如果l中下標為ll的值比r中下標為rr的值大,那麼re就應該加上l的長度n1-ll。(自己想一下為什麼)

#include

using namespace std;

const int maxn = 50003;

const int inf = 0x3f3f3f3f;

int n;

int solve(int

*a,int l,int r)

else

a[i]=l[ll++];

}/*for(int i=0;iprintf("%d ",a[i]);

printf("\n");*/

return re+sig;

}int main()

int cnt=solve(b,0,n-1);

int mi=inf;

for(int i=0;i1-(2

*a[i]));

mi=min(cnt,mi);

}printf("%d\n",mi);

}return

0;}

線段樹

這個是我看別人的題解才理解線段樹的做法的,感覺自己的線段樹還得再學學。思路是這樣的,線段樹的每個節點的值初始化為0,插入時把其對應的葉子結點更新為1,每遇到乙個a[i]就查詢當前樹裡面比它大的數有多少個,累加一下就是初始的逆序對個數了。由於是計算逆序對,所以查詢區間應該是a[i]到n-1。

#include

using

namespace

std;

const

int maxn = 5003;

const

int inf = 0x3f3f3f3f;

int sum[maxn << 2];

void pushup(int rt)

void build(int l, int r, int rt)

void update(int p,int l, int r, int rt)

int m = (l + r) >> 1;

if (p <= m) update(p, l, m, rt << 1);

else update(p, m + 1, r, rt << 1 | 1);

pushup(rt);

}int query(int ll, int rr, int l, int r, int rt)

int main()

int mi=inf;

for(int i=0;i1-(2*a[i]));

mi=min(cnt,mi);

}printf("%d\n",mi);

}return

0;}

看了一下status,線段樹在時間和空間上都是最優的,歸併排序和線段樹時間複雜度都是onlogn,但是歸併排序用到了比較多的中間陣列,所以記憶體占用比較大,可能釋放掉會比較好,有興趣可以試一下。總結一下,對這道水題我自己口胡了這麼多其實沒啥大用,如果是比賽當然是首選暴力的,連剪枝都不會考慮。但是像這樣也能拓展一下思路,順便鞏固一下其他知識,權當學習了。

序列樹hdu 1394(暴力解法)

首先宣告,我是乙個菜鳥。一下文章中出現技術誤導情況蓋不負責 嘗試用線段樹去寫,但是始終不能完全弄清標題的意思,乾脆就用暴力先過一遍,再去糾結怎麼用線段樹過。想了想還是加一下注釋,因為數列由0到n 1這n個數組成,那麼將x一道序列末尾的時候,發生的新的序列的逆序數是原序列逆序數加上n x 1,再減去x...

HDU 1394求逆序數 暴力和線段樹双解

覺得開始需要著手練下線段樹了,所以從單點更新開始著手。題目大意 給定乙個長度為n的序列,然後每次將序列當中的第乙個數放在序列末尾,這樣我們一共會得到n個序列。每個序列都會有個逆序數,取這n個數當中的最小的作為結果輸出。最最暴力的方法是把這n種情況排列出來,然後每種情況都算一下逆序數,然後取最小。由於...

POJ 2299 線段樹或樹狀陣列或歸併排序

ultra quicksort 剛剛學線性代數學到的逆序數,用多重迴圈果然超時,剛開始的時候完全沒有線段樹的思路,後來看了別人的思路,發現真的妙啊,開心的飛起來,雖然我後面又因為把小括號寫成中括號的問題wa了一晚上。比如說9 1 0 5 4這個序列,我們記錄一下他們的序號位置,然後再排個序 01 4...