CSP-X 2024 复赛编程题全解(B4104+B4105+B4106+B4107)

前言:发这篇题解是为了弥补我去年的遗憾,去年CSP-X -> 170pts. 所以今年重做一遍后 170pts. -> 400pts.

T1 [CSP-X2024 山东] 刷题:

题目传送门

思路:

这道题考差了一个非常常见的一个算法------贪心,其实这道题的贪心思路是很简单的,首先我们先确定贪心思路,那么我们先把这道题目的条件给拿出来:

  • 件物品的价格为
  • 一张优惠券元,可使用优惠券来免除件物品的价格
  • 即使物品的数量不足m个仍可以使用优惠券

然后题目让我们来求最小值,所以我们要对这些条件进行一个处理,我们肯定知道如果一张优惠券可以免除几个物品的价格的话,那么我们肯定希望让这张优惠券去满足价格大的物品,这样的话,我们自己所花的钱就会尽可能的小,这就是我们这道题的贪心思路,但是有例外情况,如果这几个物品的价格总和如果小于一张优惠券的价格的话,那么肯定单独买更划算,所以我们得对价格进行比较。

那么我们可以先进行一个从大到小的排序,尽可能的将价格大的物品用优惠券抵消掉,当然因为要比较优惠券与单独购买的价格,所以我们得在排序后将数组的前缀和求出来,然后进行比较。

代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int a[2*100005];
long long s[2*100005];
bool vis[2*100005];
bool cmp(int a,int b){
	return a>b;
}
int main(){
    int n,m,w;
    scanf("%d%d%d",&n,&m,&w);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    sort(a+1,a+n+1,cmp);//从大到小排序
    for(int i=1;i<=n;i++){//求区间和
    	s[i]=s[i-1]+a[i];
	}
    long long sum=0;//累加价格
    for(int i=1;i<=n;i++){
        if(vis[i]==1){//说明该武平已经被买过了
            continue;
        }
        if(n-i+1<m){//物品不足m个的时候
            if(s[n]-s[i-1]>=w){
                sum+=w;
                for(int j=i;j<=n;j++){
                    vis[j]=1;
                }
            }
            else{
                sum+=a[i];
                vis[i]=1;
            }
        }
        else if(s[i+m-1]-s[i-1]>=w){//物品够m个的情况
            sum+=w;
            for(int j=i;j<=i+m-1;j++){
                vis[j]=1;
            }
        }
        else{
            sum+=a[i];
            vis[i]=1;
        }
    }
    printf("%lld",sum);
    return 0;
}

T2 [CSP-X2024 山东] 消灭怪兽:

题目传送门

思路:

这道题是一道数论题目,所涉及到的内容是小学奥数中的同余问题,该题的代码难度较低,但是证明难度较高,我们需要找到所有区间和为k的倍数的区间个数,所以我们肯定是需要一个前缀和来存储区间和,以便以的时间复杂度来查询一个区间的和,那么如何保证一个区间的和是k的倍数呢?答案是,证明过程如下:

已知:

假设:

则:

所以:

注:若

那么单独也算一个答案

所以我们在求完前缀和的时候进行对于前缀和的余数进行统计,在按照加乘原理进行计数即可

代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll a[1000005];
ll s[1000005];
ll r[1000005];
int main(){
	ll n,k;
	scanf("%lld%lld",&n,&k);
	ll cnt=0;
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		s[i]=s[i-1]+a[i];//求区间和
        if(s[i]%k<0) r[(s[i]%k+k)%k]++;//余数统计(若s[i]为负数)
		else r[s[i]%k]++;//余数统计(若s[i]为正数)
	}
	for(int i=0;i<k;i++){//加乘原理进行统计 
		ll t=r[i];
		cnt+=t*(t-1)/2;
	}
	printf("%lld",cnt+r[0]);//别忘了余数为0的自己也可以组成和为k的倍数的区间
	return 0;
} 

T3 [CSP-X2024 山东] 翻硬币:

题目传送门

思路:

这道题非常巧妙地考到了一个算法------差分,其实在阅读完题目的时候,不难想到差分算法,题目的意思是这样的:

  • 个正面朝上的硬币
  • 每次选择一个区间,将区间内的硬币进行一次翻转
  • 求最后的硬币序列(正面为0,反面为1)

那么,我们需要对修改操作的时间复杂的度进行简化,那么一想到区间检修改就很容易能想到 (线段树)差分,因为修改时间复杂度为,那么这道题的思路就显而易见了,我们可以先创建一个差分数组,来保存区间修改,那么我们来将区间修改进行保存,保存结束后,对差分数组进行前缀和求解,得出来的前缀和数组在进行遍历一遍,如果是奇数,那么证明是正面,反之则是反面

代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int a[2*100005];
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    memset(a,0,sizeof a);
    while(m--){
        int l,r;
        scanf("%d%d",&l,&r);
        //差分,保存区间修改
        a[l]+=1;
        a[r+1]-=1;
    }
    int s[2*100005];
    s[0]=0;
    //前缀和求解
    for(int i=1;i<=n;i++){
        s[i]=s[i-1]+a[i];
    }
    //判断正反面
    for(int i=1;i<=n;i++){
        if(s[i]%2==1){
            printf("1");
        }
        else{
            printf("0");
        }
    }
    return 0;
}

T4 [CSP-X2024 山东] 刷题:

题目传送门

思路:

本体作为CSP-X 2024的压轴题来讲,难度还是有的,毕竟思路其实不是很好想(对于小学组正常水平同学),首先,我们先将题目的关键信息提取出来:

  • 一道编程题只能在一天内完成,不可以分多天完成
  • 可以求助小明,省去一道题的做题时间
  • 一天最多可求助小明一次(仅此一次)

我们通过这几条信息可知道这道题的答案具有单调性(最大的耗时最小),所以这道题我们可以二分,二分可以二分最小的做题时间,我们在二分的过程中,还可以借助贪心算法来对答案进行筛选,具体筛选策略如下:

  1. 如果当天可以求助小明的话肯定希望小明解决耗时最大的那道题
  2. 如果当天的时间超出了所需要检查的值的话,那么直接不做
  3. 检查是否能将所有任务全都做完

然后我们根据答案二分即可

小细节:注意二分的上线与下限,不然可能WA

代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll n,m;
ll a[100005];
bool check(ll t){//二分答案检查
    int cnt=1;//当天可以做完的任务
    for(int i=1;i<=m;i++){
        ll maxx=0;//当天耗时最长的题目
        ll sum=0;//耗时总和
        while(cnt<=n){//昨晚的任务必须小于等于n
            maxx=max(maxx,a[cnt]);//比较任务耗时大小
            if(sum+a[cnt]-maxx>t){//时间过多,不做了
                break;
            }
            sum+=a[cnt];
            cnt++;
        }
        if(cnt>n) return 1;//证明能做完
    }
    return 0;
}

int main(){
    ll l=0,r=0;
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        r+=a[i];
    }
    
    while(l<=r){//二分答案
        ll mid=(l+r)/2;
        if(check(mid)){
            r=mid-1;
        }
        else{
            l=mid+1;
        }
    }
    printf("%lld",l);
    return 0;
}
相关推荐
利刃大大16 分钟前
【c++中间件】etcd存储系统 && 服务注册 && 服务发现 && 二次封装
c++·中间件·服务发现·etcd·服务中心
Yue丶越16 分钟前
【C语言】深入理解指针(四)
java·c语言·算法
报错小能手32 分钟前
C++笔记 仿函数(函数对象)
开发语言·c++·笔记
Abona37 分钟前
自动驾驶、无人机、机器人核心技术双范式
算法·机器人·自动驾驶·无人机
草莓熊Lotso38 分钟前
《算法闯关指南:优选算法--模拟》--39.替换所有问号,40.提莫攻击
开发语言·c++·算法·模拟
艾莉丝努力练剑2 小时前
【Linux基础开发工具 (三)】Vim从入门到精通(下):效率翻倍的编辑技巧与个性化配置攻略
linux·运维·服务器·c++·ubuntu·centos·vim
yuuki2332332 小时前
【数据结构】栈
c语言·数据结构·后端
草莓熊Lotso2 小时前
C++ STL set 系列完全指南:从底层原理、核心接口到实战场景
开发语言·c++·人工智能·经验分享·网络协议·算法·dubbo
做怪小疯子4 小时前
LeetCode 热题 100——子串——和为 K 的子数组
算法·leetcode·职场和发展
zl_vslam5 小时前
SLAM中的非线性优-3D图优化之李群李代数在Opencv-PNP中的应用(四)
人工智能·opencv·算法·计算机视觉