日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[usOJ6310]冒泡排序

發(fā)布時間:2023/12/29 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [usOJ6310]冒泡排序 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目

傳送門 to usOJ

題目描述
下面是一段實現冒泡排序算法的 C++\text{C++}C++ 代碼:

for(int i=1; i<n; i++)for(int j=1; j<=n-i; j++)if(a[j] > a[j+1])swap(a[j],a[j+1]);

其中待排序的 aaa 數組是一個 1,2,3,…,n1,2,3,\dots,n1,2,3,,n 的排列,swap\tt{swap}swap 函數將交換數組中對應位置的值。

對于給定的數組 aaa 以及給定的非負整數 kkk ,使用這段代碼執(zhí)行了正好 kkkswap\tt{swap}swap 操作之后數組 aaa 中元素的值會是什么樣的呢?

輸入格式
輸入文件共 222 行。第 111 行包含空格隔開的一個正整數 nnn 和一個非負整數 kkk ;第222行包含 nnn 個空格隔開的互不相同的正整數,表示初始時 aaa 數組中的排列。

輸出格式
輸出文件共 111 行。若在執(zhí)行完整個代碼之后執(zhí)行 swap\tt{swap}swap 的次數仍不夠 ,那么輸出一個字符串 “Impossible!”\tt{“Impossible!”}Impossible!(不含引號),否則按順序輸出執(zhí)行 swap\tt{swap}swap 操作 kkk 次之后數組 aaa 的每一個元素,用空格隔開。

數據范圍與約定

對于100%100\%100%的數據,n≤106,k≤1012n\le10^6,k\le10^{12}n106,k1012

思路

給大家看一個 手畫的、丑的不堪入目的 冒泡排序過程圖——


旁邊的紅色標記是指針的意思(也就是所謂的jjj)。

你看,我們“冒泡”的過程,更像踢足球的過程。一開始球在 333 手里,然后就被 666 搶斷了;666 一路過人,過掉了比自己弱的 2,1,5,42,1,5,42,1,5,4 ,然后又被 777 搶斷了;777 射門了!結果發(fā)現是自家的門。

球回到第一個人手里,再來一次。比如圖中的情況,333 跑到了 555 前面,555 傳給了 666666 肝不動 777 ……

所以,明確這一點:“運球的球員”只會越來越大

誰會過掉你呢?是比你大的人。而且在你前面。所以我們往逆序對這方面思考。

結論:將 jjj 的循環(huán)(內層循環(huán))跑一次叫做“一輪”,每一輪中,如果 xxx 的左邊有比自己大的數字,那個比自己大的數字就會越過 xxx ,其余數字的相對位置不變;否則 xxx 就會向右移動。

首先,如果 xxx 被越過了,每一輪最多只有一個數字會越過 xxx 。因為只有一個數字在“運球”。

如果 xxx 左邊有比它大的數字,球要么去了它手上,要么是一個比它還強的球員正在運球。無論怎樣,由于運球的球員越來越大,最終這個人一定會過掉 xxx

反之,如果左邊沒有比 xxx 強的球員,xxx 一定會把球斷下來。

斷言:每一輪最多只有一個數字越過 xxx ,其充要條件是 xxx 左邊有比它大的數字;其余數字與 xxx 的相對位置不變

假設跑了 ω\omegaω ,每一個數字會被多少個數字跨過?設 bx=∑i=1x?1[ai>ax]b_x=\sum_{i=1}^{x-1}[a_i>a_x]bx?=i=1x?1?[ai?>ax?] ,顯然有兩個限制條件:

  • 答案不超過 bxb_xbx? 。因為左邊只有 bxb_xbx? 個數字可以越過它。
  • 答案不超過 ω\omegaω 。因為每一輪只有一個數字可以越過它。

所以答案就是 min?(bx,ω)\min(b_x,\omega)min(bx?,ω) 。而 swap\tt swapswap 的本質就是一次跨越,我們將其記錄在被跨過的點身上,不難發(fā)現,跨越和 swap\tt swapswap 一一對應。于是總 swap\tt swapswap 次數就是 ∑i=1nmin?(bi,ω)\sum_{i=1}^n\min(b_i,\omega)i=1n?min(bi?,ω)

所以我們可以二分 ω\omegaω 然后判斷,是否交換了至少 kkk 次。于是我們就求出了跑了多少輪。

求出多少輪之后,我們只需要知道 aaa 是什么樣子即可——最后一輪可以暴力模擬。

怎么求 aaa 數組長什么樣子呢?

對于 bx≥ωb_x\ge\omegabx?ωxxx ,因為它左邊有 ω\omegaω 個數字跨過了它,于是它就移步到了原位置向左移動 ω\omegaω 步的地方。

把這些 xxx 填好之后,就留下了一些坑,我們用剩下的數字來填。

剩下的數字是 bx<ωb_x<\omegabx?<ωxxx ,于是一定存在某個時刻,xxx 的左邊已經沒有比它小的數了。逆序對是不增的,所以直到最后,xxx 的左邊也沒有比它小的數。既然如此,剩下的數字中最小的一個,就會是最左邊的一個坑(否則更左邊的坑中就有了比它大的數字);第二小,就是左數第二個坑;其余同理。

于是我們把剩下的數字存儲下來,排序,然后依次塞到坑里去。

時間復雜度:計算 bbb 時可以使用樹狀數組,O(nlog?n)\mathcal O(n\log n)O(nlogn) ;二分與判斷,一共 O(nlog?n)\mathcal O(n\log n)O(nlogn) ;排序與填空,一共 O(nlog?n)\mathcal O(n\log n)O(nlogn)

代碼

#include <cstdio> #include <iostream> #include <vector> #include <algorithm> using namespace std; inline int readint(){int a = 0; char c = getchar(), f = 1;for(; c<'0' or c>'9'; c=getchar())if(c == '-') f = -f;for(; '0'<=c and c<='9'; c=getchar())a = (a<<3)+(a<<1)+(c^48);return a*f; } inline void writeint(long long x){if(x < 0) putchar('-'), x = -x;if(x > 9) writeint(x/10);putchar(x%10+'0'); }const int MaxN = 1000005; int a[MaxN], n; long long k;void input(){n = readint(); scanf("%lld",&k);for(int i=1; i<=n; ++i)a[i] = readint(); }int b[MaxN]; /* a[i]前面有多少個比a[i]小的數字 */ int BIT[MaxN]; /* 樹狀數組 */ void getB(){for(int i=1; i<=n; ++i){/* 經典問題:樹狀數組求逆序對 */b[i] = 0;for(int j=n+1-a[i]; j; j-=(j&-j))b[i] += BIT[j];for(int j=n+1-a[i]; j<=n; j+=(j&-j))++ BIT[j];} } long long check(int x){long long res = 0;for(int i=1; i<=n; ++i)res += min(x,b[i]);return res; } /* 多年準備一場空,不開 long long 見祖宗 */ int tmp[MaxN], bucket[MaxN]; void solve(){getB();int L = 0, R = n, mid;if(check(n) < k){ /* 即使跑n輪都搞不定k */puts("Impossible!");return;}while(L != R){ /* 二分唄,還能是啥 */mid = (L+R+1)>>1;if(check(mid) <= k)L = mid;else R = mid-1;}k -= check(L); /* 把前面那幾輪的swap次數減掉 */int jb = 0; /* 剩下的是嘉(jia)賓(bin) */for(int i=1; i<=n; ++i) /* 直接定位 or 收集 */if(b[i] >= L) tmp[i-L] = a[i];else bucket[++ jb] = a[i];sort(bucket+1,bucket+jb+1); /* 排序 */for(int i=1,ppl=1; i<=n; ++i) /* 填坑 */if(not tmp[i]) tmp[i] = bucket[ppl ++];swap(tmp,a); /* 將tmp拷貝給a,據說C++11就是O(1) */for(int i=1; i<n and k; ++i)if(a[i] > a[i+1]) /* 暴力模擬 */swap(a[i],a[i+1]), -- k;for(int i=1; i<=n; ++i)writeint(a[i]), putchar(' '); }int main(){freopen("sort.in","r",stdin);freopen("sort.out","w",stdout);input(), solve();return 0; }

總結

以上是生活随笔為你收集整理的[usOJ6310]冒泡排序的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。