可达鸭 CSP-J复赛模拟1补题报告

CSP-J复赛模拟补题报告

日期:2023.10.1星期日

1.比赛分数:

共四题,满分400,比赛中拿到120分,第一题90分,后三题每题得10分

2.比赛过程:

第一题开始想循环求素数,比赛中后来发现可以一次分解,改上后得90分。

第二题开始就没想好怎么做,想把前四各样例直接做。

第三个没想到DP,直接循环比较的。

第四题双指针暴力,也有的没有做过,仅过了1个样例。

3.题解报告:

(1)第一题:数字降级

情况:赛中90分,已补题

题意:输入一个数字,将数字除以它的任意一个因数,求多少次才能变为一个素数

题解:唯一分解定理,大于2的任何一个合数都可以分解成若干

素数相乘(最后我循环中的控制没开longlong,扣了10分)

AC代码:

#include<bits/stdc++.h>
using namespace std;
long long n,cnt=0;
bool q(long long a){
	for(long long i=2;i*i<=n;i++){
		if(n%i==0){
			return false;
		}
	}
	return true;
}
int main()
{
	cin>>n;
	if(q(n)){
		cout<<0;
	} 
	else{
		cout<<1;
	}
	return 0;
}

(2)分组

情况:赛中10分,已补题

题意:把n个数分成多组,求每组中最小的自然数加起来最大的数值

赛时想法:没想到怎么做,直接照着前四个测试点的数据测试的,但是只拿到10分

题解:因为求的是分组后最小的自然数,直接用桶标记哪些有哪些没有,循环比较每次的最小值,将最小值累加,即为最终的最大值。

AC代码:

#include<iostream>
using namespace std;
int cnt[1005];
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        cnt[x]++;
    }
    int now=cnt[0],ans=now;
    for(int i=1;i<=1000;i++){
        now=min(now, cnt[i]);
        ans+=now;
    }
    cout<<ans;
    return 0;
}

​(3)抢夺地盘

情况:赛中10分,已补题

题意:有n个城镇,实现前p个为不下降的序列,后面p到n为不上升的序列,需要多少次调换。

赛时想法:没想到这个是求最长不上升和不下降子序列的,没有用DP,直接循环比较。

题解:1~p个为求最长不下降子序列,p~n个为求最长不上升子序列,均用到DP,但数据过大,需要更加缩小时间。利用二分对每一个不下降或不上升进行存储,存储时进行比较,能使时间更优,也能增加可能性。

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int a[N], b[N], c[N];
int n, p, pos;
int main(){
    cin >> n >> pos;
    for(int i=1; i<=n; i++) cin >> a[i];
    b[++p]=a[1];
    for(int i=2; i<pos; i++){
        if(a[i]>=b[p]){
            b[++p]=a[i];
        }
        else{
            int l = 1, r = p, mid = p >> 1;
            while(l != r){
                if(a[i] < b[mid]) r = mid;
                else l = mid + 1;
                mid = (l+r) >> 1;
            }
            b[l] = a[i];
        }
    }
    if(a[pos]>=b[p]){
        p++;
    }
    else{
        a[pos]=1e9+1;
    }
    int ans=pos-p;
    p=1;
    c[p]=a[pos];
    for(int i=pos+1; i<=n; i++){
        if(a[i]<=c[p]) c[++p]=a[i];
        else{
            int l = 1, r = p, mid = p >> 1;
            while(l != r){
                if(a[i] > c[mid]) r = mid;
                else l = mid + 1;
                mid = (l + r) >> 1;
            }
            c[l] = a[i];
        }
    }
    ans+=(n-pos+1)-p;
    cout << ans;
    return 0;
}

(4)闯关

情况:赛时拿到20分,已补题。

题意:小可和达达两人分别在两个跑道进行闯关,两个人不能串跑道,并且只能向前方进行闯关,不能后退,并且假设小可和达达闯的每个关卡都可以顺利通过。小可、达达可以选择一次跃过最多 m 距离继续向后闯关,不需要每个关卡都闯过去。由于小可和达达是组队参加,小可和达达有一个闯关神器,可以让 m 距离变成 k(m<k)。开始时神器在小可的手中,小可和达达虽然分别在两个跑道,但是可以在两人距离不超过 q (k<q)时相互传递这个闯关神器。请问小可和达达都到达终点(即第 n 个关卡),最少需要使用几次闯关神器。

赛事想法:想用双指针贪心,但是在判断过关和交换时代码并不清楚,仅过两个样例。

题解:

需要用到贪心的算法,将距离和神器的位置进行贪心,尽量保证到了最远的距离再传递,并且判断如果另一人没有拿神器可不可以过关,尽量对传递的次数降低。

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int a[N], b[N], c[N];
int n, p, pos;
int main(){
    cin >> n >> pos;
    for(int i=1; i<=n; i++) cin >> a[i];
    b[++p]=a[1];
    for(int i=2; i<pos; i++){
        if(a[i]>=b[p]){
            b[++p]=a[i];
        }
        else{
            int l = 1, r = p, mid = p >> 1;
            while(l != r){
                if(a[i] < b[mid]) r = mid;
                else l = mid + 1;
                mid = (l+r) >> 1;
            }
            b[l] = a[i];
        }
    }
    if(a[pos]>=b[p]){
        p++;
    }
    else{
        a[pos]=1e9+1;
    }
    int ans=pos-p;
    p=1;
    c[p]=a[pos];
    for(int i=pos+1; i<=n; i++){
        if(a[i]<=c[p]) c[++p]=a[i];
        else{
            int l = 1, r = p, mid = p >> 1;
            while(l != r){
                if(a[i] > c[mid]) r = mid;
                else l = mid + 1;
                mid = (l + r) >> 1;
            }
            c[l] = a[i];
        }
    }
    ans+=(n-pos+1)-p;
    cout << ans;
    return 0;
}

四.赛事总结

本次比赛中进行文件储存的时候,没有再在中间套一层文件夹,导致报0,并且审题时没有想到其他做法。以后需要多审题,仔细检查文件。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值