蓝桥杯试题盾神与积木游戏之论减少时间复杂度的重要性

探讨了在有限积木条件下,如何通过贪心算法帮助每个小朋友完成作品的问题。介绍了两种排序方法:冒泡排序与合并排序,并分析了其时间复杂度对算法效率的影响。

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

问题描述

最近的m天盾神都去幼儿园陪小朋友们玩去了~
  每个小朋友都拿到了一些积木,他们各自需要不同数量的积木来拼一些他们想要的东西。但是有的小朋友拿得多,有的小朋友拿得少,有些小朋友需要拿到其他小朋友的积木才能完成他的大作。如果某个小朋友完成了他的作品,那么他就会把自己的作品推倒,而无私地把他的所有积木都奉献出来;但是,如果他还没有完成自己的作品,他是不会把积木让出去的哟~
  盾神看到这么和谐的小朋友们感到非常开心,于是想帮助他们所有人都完成他们各自的作品。盾神现在在想,这个理想有没有可能实现呢?于是把这个问题交给了他最信赖的你。

输入格式

第一行为一个数m。
  接下来有m组数据。每一组的第一行为n,表示这天有n个小朋友。接下来的n行每行两个数,分别表示他现在拥有的积木数和他一共需要的积木数。

输出格式

输出m行,如果第i天能顺利完成所有作品,输出YES,否则输出NO。

样例输入

2
2
2 2
1 3
3
1 5
3 3
0 4

样例输出

YES
NO

数据规模和约定

1<=n<=10000,1<=m<=10。

第一次提交

在这里插入图片描述

#include <iostream>
#include<string>
using namespace std;
typedef struct
{
    int now;//该小朋友目前手中所有的积木
    int all;//该小朋友完成创作一共需要的积木数量
    int c;//该小朋友完成创作还差多少积木
}tak;
//冒泡排序
void Sort(tak *p,int n)
{
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n-i;j++)
    {
        if(p[j].c>p[j+1].c)
            swap(p[j],p[j+1]);
    }
}
int main()
{
    tak p[10000];
    int m;
    cin>>m;
    string s[m];//用来保存m天的输出结果
    for(int t=0;t<m;t++)
    {
        int n;
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>p[i].now>>p[i].all;
            p[i].c=p[i].all - p[i].now;//构造还小朋友需要的积木数量
        }
        //按照p[i].c升序排序
        //整体求解的算法思路为贪心算法,比较简单
        Sort(p,n);//按照还需要的积木数量对小朋友进行升序排序
        int i,ex=0;
        for(i=1;i<=n;i++)//注意如果正常跳出循环i=n+1
        {
            if(ex>=p[i].c){
                ex=ex+p[i].now;
            }else{
                s[t]="NO";
                break;//贪心不下去了
            }
        }
        if(i>n) s[t]="YES";//所有小朋友都完成的创作
        else s[t]="NO";//所有小朋友有人未完成创作
    }

    for(int i=0;i<m-1;i++)
        cout<<s[i]<<endl;//m-1行
    cout<<s[m-1];//第m行
    return 0;
}

在这里插入图片描述
笔者想了想,既然能够通过6组数据,那么算法肯定是没有问题的,问题肯定出现在时间复杂度上面。按照这一次提交的代码,需要对所有的小朋友按照还需要的积木数量进行升序排序,我写的冒泡排序O(n2),再一次遍历需要O(n),一共有m组数据,所以算法的时间复杂度为O(m*n2)

第二次提交

这一次我将冒泡排序改成了合并排序,合并排序的时间复杂度为O(nlogn),一共有m组数据,所以算法的时间复杂度为O(m*nlogn)

#include <iostream>
#include<string>
using namespace std;
typedef struct
{
    int now;//该小朋友目前手中所有的积木
    int all;//该小朋友完成创作一共需要的积木数量
    int c;//该小朋友完成创作还差多少积木
}tak;
//把时间复杂度降低
//三个指针实现归并排序
void merge(tak a[],int l,int r,int mid)
{
  tak temp[r-l+1];
  int i,j,k;
  i=l;//左半部分指针
  j=mid+1;//右半部分指针
  //k是临时数组指针
  k=0;//临时数组初始化为0
  while(i<=mid&&j<=r&&k<r-l+1)
  {
    if(a[i].c<a[j].c)
        temp[k++]=a[i++];
    else
        temp[k++]=a[j++];
  }
  while(i<=mid)
    temp[k++] = a[i++];
  while(j<=r)
    temp[k++] = a[j++];
    //把临时数组再复制回去a数组
    k=0;
    for(int p=l;p<=r;p++)
        a[p]=temp[k++];
}
//分治递归,合并排序
void merge_sort(tak a[],int l,int r)
{
    if(l>=r) return ;
	int mid=(l+r)/2;
	merge_sort(a,l,mid);
	merge_sort(a,mid+1,r);
    merge(a,l,r,mid);//这一步实现了合并临时数组和复制临时数组
}
int main()
{
    tak p[10000];
    int m;
    cin>>m;
    string s[m];//用来保存m天的输出结果
    for(int t=0;t<m;t++)
    {
        int n;
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>p[i].now>>p[i].all;
            p[i].c=p[i].all - p[i].now;//构造还小朋友需要的积木数量
        }
        //按照p[i].c升序排序
        //整体求解的算法思路为贪心算法,比较简单
        merge_sort(p,1,n);//按照还需要的积木数量对小朋友进行升序排序
        int i,ex=0;
        for(i=1;i<=n;i++)//注意如果正常跳出循环i=n+1
        {
            if(ex>=p[i].c){
                ex=ex+p[i].now;
            }else{
                s[t]="NO";
                break;//贪心不下去了
            }
        }
        if(i>n) s[t]="YES";//所有小朋友都完成的创作
        else s[t]="NO";//所有小朋友有人未完成创作
    }

    for(int i=0;i<m-1;i++)
        cout<<s[i]<<endl;//m-1行
    cout<<s[m-1];//第m行
    return 0;
}

在这里插入图片描述

这一次,居然所有数据都通过了。通过这次实验,空间复杂度科研牺牲,但是时间复杂度必须尽可能降低。特别是贪心算法,时间复杂度的花费都集中在排序上面。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值