[USACO18OPEN]Out of Sorts G 冒泡排序理解之一

本文介绍了一种改进的双向冒泡排序算法,并通过树状数组来优化其性能。这种排序方式不仅每次将最大元素置于正确位置,同时也会处理最小元素。文章详细解析了算法的工作原理及其实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

给一个双向冒泡排序的程序:

moo表示输出moo

sorted = false
while (not sorted):
   sorted = true
   moo
   for i = 0 to N-2:
      if A[i+1] < A[i]:
         swap A[i], A[i+1]
   for i = N-2 downto 0:
      if A[i+1] < A[i]:
         swap A[i], A[i+1]
   for i = 0 to N-2:
      if A[i+1] < A[i]:
         sorted = false

再给一个初始的数组。

求把这个数组排好序,输出几次moo

n<=100000,ai<=1e9

题解

考察冒泡排序的本质。

一般的冒泡排序,每次会把最大的数直接沉底,

但是比较小的数,会往前面缓慢冒泡。

具体来说,如果一个数i排名是i,但是位置在i之后,那么,每次循环,i必然会往前面移动一位。

因为必然前面会有一个比i大的数往后沉。

 

i往前每次移动一次导致复杂度最坏是n^2

swap的次数,就是逆序对数

循环的次数,就是

 

这个题目,是一个双向的冒泡排序,

对于一个分界点位置i,这个双向排序,每个循环会把一个i之前的大于i的数移到i后面,并且把一个i后面小于i的数移到i的前面。

单向冒泡不能保证每次把一个i后面的小于i的数放到i前面。

而拍好序是什么意思?

就是说,对于任意的位置i,小于i的数都在i前面,大于i的数都在i后面。

所以 ,我们可以对于所有的位置i,找到i前面比i小的数个数s,i-s就是i位置把小于i的放在i前面,大于i的放在i后面的循环次数。

对于这些次数取一个mx即可。

用树状数组离散化后可以做。

#include<bits/stdc++.h>
using namespace std;
const int N=100000+5;
int n,ans;
int a[N],b[N],cnt[N];
int f[N];
void add(int x){for(;x<=n;x+=x&(-x)) f[x]++;}
int query(int x){int ret=0;for(;x;x-=x&(-x))ret+=f[x];return ret;}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);b[i]=a[i];
    }
    sort(b+1,b+n+1);ans=1;
    for(int i=1;i<=n;i++){
        int t=lower_bound(b+1,b+n+1,a[i])-b;
        //cout<<" tt "<<t<<endl;
        add(t);
        cnt[i]=query(i);ans=max(ans,i-cnt[i]);
    }printf("%d",ans);return 0;
}

 

注意,当数组排好序之后,因为一定要检查一遍,所以moo还是1的。不是0

 

代码:

 

转载于:https://www.cnblogs.com/Miracevin/p/9662350.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值