冒泡排序

USACO2018有三道关于冒泡排序的题,做完感觉排序白学了

1.[USACO18OPEN]Out of Sorts S

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]
         sorted = false

给出数列,问这个循环会执行多少次。

通过观察,我们能够轻易得出,在每次执行冒泡时,大数会很快跑到后面去,小数一次只能前进一次。(不明白手玩)。

所以答案就是每个当前位置减去排序后位置去max。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 100002
using namespace std;
int ans,n;
struct zzh{
    int a,b;
}a[N];
bool cmp(zzh x,zzh y){
    if(x.a!=y.a)return x.a<y.a;
    else return x.b<y.b;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d",&a[i].a),a[i].b=i;
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;++i)
      ans=max(ans,a[i].b-i+1);
    cout<<ans;
    return 0;
}

2.[USACO18OPEN]Out of Sorts G

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

把冒泡排序改了一下,问会进行几次循环。

冒泡排序的缺点就是小数前进的很慢,这个代码每次正向冒泡一遍,在反着扫一遍,看似把这个问题解决了。

看起来计算会比较复杂,不如换一种思考方式。

我们把每个数映射为1-n中的每个数,排完序后肯定是a[i]=i。

在定义一个东西叫分割点,一个位置i成为分割点,当且仅当1-i中所有数值域为1-i。

这样区域之后就不会和其他区域交换了。

当每个数都是分割点时,我们的排序就完成了。

考虑一个点i,设1-i中大于i的数有x个,如果我们用普通的冒泡排序,一轮之后x中一定会有一个数被换出去,还有一个数被换了进来。

但因为算法的局限性,我们不能保证换进来的数是1-i中的。

但双向冒泡排序就避免了这个问题,一旦有一个“非法”的数被换了进来,在反向扫描时会被一个“合法”的数换掉。

这样每次冒泡不合法的数就减少了一。

这样统计答案变得轻而易举。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 100002
using namespace std;
int n,tr[N],ans;
struct zzh{
    int a,b;
}a[N];
bool cmp(zzh a,zzh b){
    if(a.a!=b.a)return a.a<b.a;
    else return a.b<b.b;
}
inline void add(int x,int y){while(x<=n)tr[x]+=y,x+=x&-x;}
inline int q(int x){int ans=0;while(x)ans+=tr[x],x-=x&-x;return ans;} 
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d",&a[i].a),a[i].b=i;
    sort(a+1,a+n+1,cmp);
    ans=1;
    for(int i=1;i<=n;++i){
        add(a[i].b,1);
        ans=max(ans,i-q(i));
    }
    cout<<ans;
    return 0;
}

3.[USACO18OPEN]Out of Sorts G

quickish_sort (A) {
   if length(A) = 1, return
   do { // Main loop
      work_counter = work_counter + length(A)
      bubble_sort_pass(A)
   } while (no partition points exist in A) 
   divide A at all partition points; recursively quickish_sort each piece
}

这哥们在快速排序里面套了个冒泡排序。

每次冒泡的贡献是冒泡序列的长度。

循环退出的条件是存在分割线,分割线的定义:一个点成为分割点,当且仅当前缀最大值小于后缀最小值。

那么左右两个区间就没有关系了。

这题的思路是统计没个元素的贡献,最后求和。

当一个元素没有贡献时,它的长度为一(不然会被继续冒泡)。

所以他的贡献为它的前面成为分割线的时间和后面成为分割线的时间取max。

那我们就计算每个位置成为分割线的时间。

然后突然发现好像回到了上一题。

唯一不同的是这是普通的冒泡排序,每个小数每次只能前进1个单位。

所以每个分界线的答案就是max(pos[1-i])-i;

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 100002
using namespace std;
int n,t[N],qmax;
long long ans;
struct zzh{
    int a,b;
}a[N];
bool cmp(zzh a,zzh b){
    if(a.a!=b.a)return a.a<b.a;
    else return a.b<b.b;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d",&a[i].a),a[i].b=i;
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;++i){
        qmax=max(qmax,a[i].b);
        t[i]=qmax-i;
    }
    for(int i=1;i<=n;++i){
        int x=max(t[i],t[i-1]);
        if(!x)x++;
        ans+=x;
    }
    cout<<ans;
    return 0;
}

 

转载于:https://www.cnblogs.com/ZH-comld/p/9661966.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值