计算冒泡排序的交换次数:
逆序数概念:在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序
一个排列中所有逆序总数叫做这个排列的逆序数。 所以冒泡排序结束即是所有的逆序数为0
思路:
暴力:我们可以开一个vis[]数组记录 在遍历到第 i 位时,已经出现的值有哪些。然后遍历到 arr[i] (第i位的值),得到小于arr[i] 的出现个数(即 i<j && arr[i]<= arr[j]的个数)
然后再用当前长度 i 减去符合条件个数即得打 arr[i]的逆序个数。
优化:由于每次都要对小于 arr[i]的出现元素遍历,我们就希望能够开一个数组 记录arr[i]之前出现的符合条件个数(这样就不用每次都遍历一次取求)
那么对于单点更新,求区间和 ,我们就可以借助 树状数组 这个数据结构。所以就利用 树状数组进行优化。
操作:
求出数列种每一个数的逆序数求和
遍历a[i],a[i]再bit[i]中对应的值加一
记录每个值出现前小于自身,自身出现次数
遍历到j时a[j]前有j个数,即小于自身个数有bit[j]个
逆序数为 j - bit[j]
//计算冒泡排序的交换次数: //逆序数概念,冒泡排序结束即是所有的逆序数为0 //思路:求出数列种每一个数的逆序数求和 //遍历a[i],a[i]再bit[i]中对应的值加一 //记录每个值出现前小于自身,自身出现次数 //遍历到j时a[j]前有j个数,即小于自身个数有bit[j]个 //逆序数为 j - bit[j] #include <bits/stdc++.h> #define IOS ios::sync_with_stdio(0); cin.tie(0); #define mp make_pair #define Accept 0 using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> pii; const double Pi = acos(-1.0); const double esp = 1e-9; const int inf = 0x3f3f3f3f; const int maxn = 1e5+7; const int maxm = 1e6+7; const int mod = 1e9+7; const int MAXL = 1e6+7; int n; int bit[maxn]; int arr[maxn]; int vis[maxn]; //求和即是从i开始,将其二进制最低位的1去掉,直到变为0 //前i项和 int sum(int i){ int s = 0; while(i>0){ s += bit[i]; i -= i & -i; } return s; } //单点跟新,将第i位增加x,将最低为的非0幂加到对应的幂上 void add(int i,int x){ //bit[i] = x; 单点修改 //i += i & -i; while(i<=n){ bit[i] += x; i += i & -i; } } void solve(){ int ans = 0; for(int j = 0;j<n;j++){ ans += j - sum(arr[j]); add(arr[j],1); }//暴力的解法 // for(int i=0;i<n;i++){ // int cnt = 0; // for(int j=0;j<arr[i];j++){ // if(vis[j]) cnt++; // } // ans += i - cnt; // vis[arr[i]] =1; // } printf("%d\n",ans); } int main(){ memset(vis,0,sizeof(vis)); scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d",&arr[i]); } solve(); return 0; }
原文地址:https://www.cnblogs.com/Tianwell/p/11490987.html
时间: 2024-10-10 01:27:06