2075:【21CSPJ普及组】插入排序(sort)(三种做法,层层优化)


 一,普通暴力模拟(52分)

最简单的做法,直接看代码吧。

#include <bits/stdc++.h>
using namespace std;
//p[i]表示排序后i位置的数在原数组p[i]位置
//np[i]表示原数组i位置的数在排序后的数组np[i]
int a[10010], b[10010], p[10010], np[10010], bp[10010], bnp[10010],n, q;
int main()
{
  cin>>n>>q;
  for(int i = 1; i <= n; i ++)
  {
    scanf("%d", &a[i]);
    np[i] = p[i] = i;
  }
  while(q--)
  {
    int op, x, v;
    scanf("%d%d", &op, &x);
    if(op == 1)
    {
      scanf("%d", &v);
      a[x] = v;
    }
    else
    {
      memcpy(b, a, sizeof a);
      memcpy(bp, p, sizeof p);//提前备份
      memcpy(bnp, np, sizeof np);
      for(int i = 2; i <= n; i ++)
        for(int j = i; j > 1 && a[j] < a[j - 1]; j --)
        {
          swap(a[j], a[j - 1]);
          np[p[j]] = j - 1;//插入排序
          np[p[j - 1]] = j;
          swap(p[j], p[j - 1]);
        }
      printf("%d\n", np[x]);
      memcpy(a, b, sizeof b);
      memcpy(p, bp, sizeof bp);//还原
      memcpy(np, bnp, sizeof bnp);
    }
  }
  return 0;
}

 


 二,优化掉归并(76分)

通过观察我们可以发现,在这里耗时的主要是归并排序(时间复杂度可是O(N^2)呢!),所以我们可以不用归并来做第二个操作,直接计算有多少个数字>=它,最后再加一就可以求出师原来 a 的第 x 个元素,也就是 a[x]在排序后的新数组所处的位置。

代码:


#include <bits/stdc++.h>
using namespace std;
int a[10010],n,q;
int main()
{
  cin>>n>>q;
  for(int i = 1; i <= n; i++) scanf("%d",&a[i]);
  while(q--)
  {
    int op,x,v;
    scanf("%d%d", &op, &x);
    if(op == 1)
    {
      scanf("%d", &v);
      a[x] = v;
    }
    else
    {
      int con = 0;
      for(int i = 1; i < x; i++)
        if(a[i] <= a[x])
          con++;
      for(int i = x + 1; i <= n; i++)
        if(a[i] < a[x])
          con++;
      printf("%d\n",con + 1);
    }
  }
  return 0;
}

 


三,正解

我们要定义一个结构体数组,有两个元素,分别是需要存储的量和它的序号,还需要定义一个b数组,用来存放他们排序后的序号。

初始化

操作二需要把a数组进行排序,可操作二的次数不确定,会超时,所以我们应该把排序的任务放在操作一之后以及输入之后。所干的事情应该是把a数组的元素进行排序,可以用sort函数,并且把a数组的下标存入b数组里。

进行操作

首先进行q次循环,可以用while。每次循环,先输入操作类型,然后判断操作类型是一还是二。

操作一
输入x和v先进行修改的任务,只需将原来a数组的第x个元素改v,再重新进行排序,再把存好的a数组的下标存起来,但排序a数组时不能再使用sort函数了,因为这样会超时,那么应该怎么优化呢,不难看出,a数组的元素中,只有一个修改的数是乱的,其余的数排序都是升序排好的,所以我们可以使用两个for循环,一个正着遍历一遍,另一个反着遍历一遍,如果这个数字比前面一个数小,则反着遍历的for可以把它存到适当的位置;反之,如果这个数字比后一个数还要大,则正序遍历的for可以把它存到适当的位置。而这两个for是顺序关系,时间复杂度:O(2n)

操作二

如果是操作二的话,只需要输出b数组里的下标就行了。

代码:

#include <bits/stdc++.h>
using namespace std;
struct dot//建立结构体数组,把数值和编号封装起来
{
  int num,id;//num:数值   id:位置
} a[1000001];
bool cmp(dot a,dot b)//用sort给结构体数组排序(数值相等会按编号排序)
{
  if(a.num != b.num) return a.num < b.num;
  return a.id < b.id;
}
int n,q,b[100001],op,x,v;
int main()
{
  scanf("%d%d",&n,&q);
  for(int i = 1; i <= n; i++)
  {
    cin>>a[i].num;
    a[i].id = i; //a数组id初始化
  }
  sort(a + 1,a + n + 1,cmp);
  for(int i = 1; i <= n; i++) b[a[i].id] = i; //b数组用来存排序后各数值在排序前的位置
  while(q--)
  {
    scanf("%d",&op);
    if(op == 1)
    {
      scanf("%d%d",&x,&v);
      if(a[b[x]].num < v)//v比原来的a[x]的值大,则向后移动,使数组有序
      {
        a[b[x]].num = v;//赋值 
        for(int i = b[x]; i < n; i++)//从b[x]向后移动 
          if(cmp(a[i + 1],a[i]))
            swap(a[i],a[i + 1]);
          else break;//说明数组已经有序了 
      }
      else//v比原来的a[x]的值小,则向前移动,使数组有序
      {
        a[b[x]].num = v;//赋值 
        for(int i = b[x] - 1; i > 0; i--)//从b[x] - 1向前移动 
          if(cmp(a[i + 1],a[i]))
            swap(a[i],a[i + 1]);
          else break;//说明数组已经有序了 
      }
      for(int i = 1; i <= n; i++) b[a[i].id] = i; //更新b数组
    }
    else
    {
      scanf("%d",&x);
      printf("%d\n",b[x]);//这样第二个操作就可以直接O(1)解决了 
    }
  }
  return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值