第五屆 小朋友排隊

2021-07-10 05:42:00 字數 3123 閱讀 7232

目前不會!!

n 個小朋友站成一排。現在要把他們按身高從低到高的順序排列,但是每次只能交換位置相鄰的兩個小朋友。每個小朋友都有乙個不高興的程度。開始的時候,所有小朋友的不高興程度都是0。如果某個小朋友第一次被要求交換,則他的不高興程度增加1,如果第二次要求他交換,則他的不高興程度增加2(即不高興程度為3),依次類推。當要求某個小朋友第k次交換時,他的不高興程度增加k。請問,要讓所有小朋友按從低到高排隊,他們的不高興程度之和最小是多少。如果有兩個小朋友身高一樣,則他們誰站在誰前面是沒有關係的。

【資料格式】

輸入的第一行包含乙個整數n,表示小朋友的個數。

第二行包含 n 個整數 h1 h2 … hn,分別表示每個小朋友的身高。

輸出一行,包含乙個整數,表示小朋友的不高興程度和的最小值。

【資料規模與約定】

對於10%的資料, 1<=n<=10;

對於30%的資料, 1<=n<=1000;

對於50%的資料, 1<=n<=10000;

對於100%的資料,1<=n<=100000,0<=hi<=1000000。

例如,輸入:

33 2 1

程式應該輸出:

9

題目分析:

這是一道尋找逆序數的題目,比如題目中的3,2,1,和3有關的逆序對為3,2和3,1,和2有關的逆序對為3,2和2,1,和1有關的逆序對為3,1和2,1,為了完成排序,任何乙個逆序對的兩個元素都必須交換一次,於是可知,每個小朋友都完成了兩次交換。

說白了就是針對每乙個元素,看看它之前有幾個大於它的元素,之後有幾個小於它的元素,然後記錄這個元素在逆序對中出現的次數,也就是含這個元素的逆序對的個數,然後再將每個元素的不高興程度相加即可。問題的關鍵在於查詢逆序對的個數,最容易想到的當然就是暴力**,看看資料範圍,超時無疑,於是我們就需要使用乙個特殊的陣列----樹狀陣列。

樹狀陣列的特點就是很快速的求前n個元素的和,那這和我們求逆序對有什麼關係呢?

這裡,我們要巧妙地變一下形,還是以題目中給出的資料為例  3   2   1。

因為樹狀陣列實際上是由兩部分組成:資料陣列+統計陣列,我們只看資料陣列。由於樹狀陣列是從1開始的,而題目中小盆友的身高可以為0(真是長見識了),所以我們將每個小盆友的身高加1然後作為樹狀陣列的下標,將數值1存到相應的位置.

第一次讀入3,此時讀入的資料量為1個,變成這樣

c[1]    c[2]     c[3]       c[4]       c[5]        c[6]     c[7]     c[8]

0      0      0           1         0         0       0       0

可以看到sum(c[1],c[4])=1(可以由樹狀陣列的統計陣列得到),這個是小於等於3的數字的個數,也就是說當輸入第乙個數字3的時候沒有比它小的數字存在,這時我們用 輸入數字總數-sum(c[1],c[4])=0,也就是說大於3的數字的個數為0,我們令b[0]=0.

第二次讀入2,此時讀入的資料量為2個,變成這樣

c[1]     c[2]     c[3]        c[4]       c[5]      c[6]         c[7]         c[8]

0       0       1        1          0         0           0         0

可以看到sum(c[1],c[3])=1,任然不存在比它小的數,但此時輸入的資料總量為2,而2-1=1,就是說,存在乙個數在2之前並且大於2,這個數當然就是3,我們另b[1]=1.

第三次讀入1,此時讀入的資料量為3,變成這樣

c[1]   c[2]   c[3]   c[4]   c[5]    c[6]    c[7]   c[8]

0    1     1     1     0       0      0     0

可以看到sum(c[1],c[2])=1,任然不存在比它小的數,但此時輸入的資料總量為3,而3-1=2,就是說,存在兩個數在1之前並且大於1,這個數當然就是2,3,我們另b[2]=2.

到此,我們已經算出了每個數前面的較大的數的個數了,資料存在num中,現在我們再反過來,先插入1,再插入2,再插入3,但這次我們不再用總數減去sum了,而直接求sum,求出來的自然就是,每個數後面的較小的數的個數,然後將得到的數值累加到相應的b[i]中,最終我們會得到b[0]=2,b[1]=2,b[2]=2,分別對應num[0]=3,num[1]=2,num[2]=1。

求得了每個小盆友被移動的次數,我們需要計算其不高興程度,這裡實際上可以事先打個表,就是將被移動n次後的不高興值全算出來,然後直接用就可以了,這裡,我們將其存到total陣列中,而且total[2]=3,所以總不高興值就是9.

需要注意的是如果重複的數字出現怎麼辦,如果出現,實際上出問題的會是求每個數之前較大數的那部分,因為用到了數的總個數,如果出現一樣的數,就會導致相減後的結果偏大,而且正好是大了 重複量-1 ,那麼我們就可以算出重複量,然後將這一部分減去就行,關鍵是怎麼算重複量,實際也很簡單,通過樹狀陣列,我們可以求得sum(1,a)和sum(1,a+1),其中輸入的數字為a,前者算出的小於a的數的個數,後者算出的是小於等於a的數的個數,兩個一減就是等於a的個數。

#include#include#define max 1000010

#define n 100010

using namespace std;

int c1[max],c2[max],b[max];

int num[n];

long long total[n],ans;

int lowbit(int x)

void add(int pos,int num,int *c)

}int sum(int pos,int *c)

return sum;

}void init()

}int main()

//for(int j=0;j<20;j++)

// cout

for(i=0;icout

}

小朋友排隊 第五屆藍橋杯

第一種,直接思考的方法,不知對不正確 n 個小朋友站成一排。如今要把他們按身高從低到高的順序排列,可是每次僅僅能交換位置相鄰的兩個小朋友。每乙個小朋友都有乙個不高興的程度。開始的時候。全部小朋友的不高興程度都是0。假設某個小朋友第一次被要求交換,則他的不高興程度新增1,假設第二次要求他交換。則他的不...

小朋友排隊 第五屆藍橋杯b組

氣泡排序 要交換的次數等於序列中逆序對的個數。設逆序對數量k,所以交換次數至少是k。每一次交換後逆序對數量必然減一。所以總數最小是k。include include include includeusing namespace std typedef long long ll const int n...

小朋友排隊

n 個小朋友站成一排。現在要把他們按身高從低到高的順序排列,但是每次只能交換位置相鄰的兩個小朋友。每個小朋友都有乙個不高興的程度。開始的時候,所有小朋友的不高興程度都是0。如果某個小朋友第一次被要求交換,則他的不高興程度增加1,如果第二次要求他交換,則他的不高興程度增加2 即不高興程度為3 依次類推...