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,并且审题时没有想到其他做法。以后需要多审题,仔细检查文件。