題意:給定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...