本文已彻底 沦为 变成博主的习题册,清转头阅读其他文章哦~啾咪~
参考了文章:https://blog.csdn.net/qq_41550842/article/details/81215935
归并排序的理解
(图源见水印)
归并排序,可以看这个网站的动图理解:https://visualgo.net/zh/sorting?slide=10-2
归并排序是分治算法的一种体现,分治解题的一般步骤是:
- 划分步骤:将大的原始问题划分成较小的子问题并递归地解决较小的子问题,
- 解决步骤:结合较小的子问题的结果来产生较大的原始问题的结果。
而归并排序的步骤可以细化为:
- 划分问题:把序列分成元素个数尽量相等的两半
- 递归求解:把两半元素分别排序
- 合并问题:把两个有序表合并为一个
在合并两边已排好的元素时,因为两个序列都具有不下降性,所以只需要比较两个序列里最小的元素就可以。
对于实现,一般需要一个辅助数组;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; const int N = 1000005; int a[N] ,b[N];//b为辅助数组 void merge_sort(int l , int r) { if(l < r){ //如果整个区间中元素个数大于1,则继续分割 int mid = (l + r) >> 2 ; //位运算提高速度 int i = l; //辅助数组的下标 int p = l , q = mid+1; merge_sort(l,mid); merge_sort(mid+1 ,r); while(p<=mid || q<=r){//左右两部分只要有一部分不为空 if(q>r || (p<=mid && a[p]<=a[q]))//右边空了或者右边的数更大一点,就把左边的这个数放到前面一点点,(从左半数组复制到辅助数组) b[i++] = a[p++]; else b[i++] = a[q++];//从右半数组复制到辅助数组 } for(i = l ; i <= r; i++)//将b中排好序的元素复制到a中 a[i] = b[i]; } } int main() { int n; scanf("%d",&n); for(int i = 1 ; i <= n; i ++) cin >> a[i]; merge_sort(1 , n); for(int i = 1; i <= n; i++) cout << a[i] << " "; return 0; }
归并排序的应用
1.求逆序对
(听说可以用树状数组,然鹅小蒟蒻还没有学,学了以后再补一个叭qwq)
在比较的时候考虑:如果左边最小的数都比右边这个数大,那么左边剩余的所有数都会比右边这个数大
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; #define N 1000005 int a[N] ,b[N]; long long cnt; void merge_sort(int l , int r) { if(l < r){ int mid = (l + r) / 2 ; int i = l; int p = l , q = mid+1; merge_sort(l , mid); merge_sort(mid+1 , r); while(p<=mid || q<=r){ if(q > r || (p <= mid && a[p] <= a[q])) b[i++] = a[p++]; else{ b[i++] = a[q++]; cnt += mid -p +1; //将逆序对的个数累加起来 } } for(i = l ; i <= r; i++) a[i] = b[i]; } } int main() { int n; scanf("%d",&n); for(int i = 1 ; i <= n; i ++) cin >> a[i]; cnt = 0; merge_sort(1 , n); printf("%lld",cnt); return 0; }
[求逆序对·进阶版] noip2013火柴排队
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//火柴 #include<bits/stdc++.h> const int N = 200005; const int mod = 99999997; struct node{ int x,p; }; node a[N],b[N]; int n,c[N],f[N],ans; int cmp(node a,node b){ return a.x < b.x;} void m_sort(int l,int r){ if(l == r) return ; int mid = (l + r)/ 2; //这个地方不可以改哦 int i = l, j = mid + 1,k = l; m_sort(l,mid); m_sort(mid+1,r); while(i <= mid && j <= r){ if(c[i] <= c[j]) f[k++] = c[i++]; else { f[k++] = c[j++]; ans += mid - i + 1; ans %= mod; } } while(i <= mid) f[k++] = c[i++]; while(j <= r ) f[k++] = c[j++]; for(int i = l; i <= r; i++) c[i] = f[i]; } signed main(){ scanf("%d",&n); for(int i = 1; i <= n; i++) { scanf("%d",&a[i].x); a[i].p =i; } for(int i = 1; i <= n; i++) { scanf("%d",&b[i].x); b[i].p =i; } std::sort(a+1,a+n+1,cmp); std::sort(b+1,b+n+1,cmp); for(int i = 1; i <= n; i++) c[a[i].p] = b[i].p; m_sort(1,n); printf("%d",ans); return 0; }
2.只涉及相邻元素的排序
这种情况下快排会浪费很多时间,而归并正是解决这种问题的利器。
e.g. NOIP2011瑞士轮
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<algorithm> using namespace std; #define fr(i,n) for(register int i = 1; i <= n; ++i) int n,r,q; const int N = 200500; int a[N],win[N],lose[N],c[N],w[N]; int tot; bool cmp(int x,int y) { if(c[x]==c[y]) return x<y; return c[x]>c[y]; } void merge(){ int l = 1, r = 1, k = 0; while(l <= tot && r <= tot) { if(cmp(win[l],lose[r])) a[++k] = win[l++]; else a[++k] = lose[r++]; } while(l <= tot) a[++k] = win[l++]; while(r <= tot) a[++k] = lose[r++]; } int main(){ scanf("%d%d%d",&n,&r,&q); n = n * 2; fr(i,n) a[i] = i; fr(i,n) scanf("%d",c+i); fr(i,n) scanf("%d",w+i); sort(a+1,a+1+n,cmp); //a[i]记录的是排名为i的选手是哪位 while(r--){ tot = 0; for(register int i = 1; i <= n; i+=2){ //跳二是排名紧靠着的两位选手一起比赛 if(w[a[i]] > w[a[i+1]]){ win[++tot] = a[i]; //这两行 记录了赢和输这一场的分别是哪个选手 lose[tot] = a[i+1]; c[a[i]]++; //分数++ } else{ win[++tot] = a[i+1]; lose[tot] = a[i]; c[a[i+1]]++; } } merge(); } printf("%d ",a[q]); return 0; }
#include<iostream> #include<algorithm> using namespace std; #define fr(i,n) for(register int i = 1; i <= n; ++i) int n,r,q; const int N = 200500; int a[N],win[N],lose[N],c[N],w[N]; int tot; bool cmp(int x,int y) { if(c[x]==c[y]) return x<y; return c[x]>c[y]; } void merge(){ int l = 1, r = 1, k = 0; while(l <= tot && r <= tot) { if(cmp(win[l],lose[r])) a[++k] = win[l++]; else a[++k] = lose[r++]; } while(l <= tot) a[++k] = win[l++]; while(r <= tot) a[++k] = lose[r++]; } int main(){ scanf("%d%d%d",&n,&r,&q); n = n * 2; fr(i,n) a[i] = i; fr(i,n) scanf("%d",c+i); fr(i,n) scanf("%d",w+i); sort(a+1,a+1+n,cmp); while(r--){ tot = 0; for(register int i = 1; i <= n; i+=2){ if(w[a[i]] > w[a[i+1]]){ win[++tot] = a[i]; lose[tot] = a[i+1]; c[a[i]]++; } else{ win[++tot] = a[i+1]; lose[tot] = a[i]; c[a[i+1]]++; } } merge(); } printf("%d ",a[q]); retur