日期:2023 年 10 月 1 日星期日
学号:S07246
姓名:江守栋
1. 比赛概况:
比赛总分共4 题,满分400,赛时拿到220分,其中第一题100分,第二题
100 分,第三题10分,第四题10分。
2. 比赛过程:
这场比赛我把重点放在了前两道题上,拿到题目之后先看了第一题,一开始以为 需要模拟或枚举,做着做着忽然恍然大悟,想到了唯一分解定理,写完了第 一题,并在赛时AC。
第二题,我研究了样例,找到了思路和方法,很快就写完了代码,但调试了很 久,我以为是算法部分出了问题,却发现只是freopen读入部分写错了,好在最 后也AC了。
后两题我看了很久,但没有思路,直接输出了样例,总共得了20分。
3. 题解报告:
(1) 第一题:数字降级
情况:赛中100分
题意:数字每一次降级都表示将一个数字除以一次它的任意一个因子。
请问最少几次操作可以将一个数字 ,降级成一个质数?
赛时本题做题想法: 原本想采用模拟,后来想起了唯一分解定理,分为两种情况:1:N为质数,不需要操作,输出0 2:N不为质数,输出1 轻松AC了这道题
题解:这道题用到了唯一分解定理,即任何一个大于1的自然数 N,如果N不为质数,那么N可以唯一分解成有限个质数的乘积,所以可以写一个判断质数的函数,如果N是质数,则不需要降级,直接输出0。如果不为质数,根据唯一分解定理,N一定等于一个质数乘另外一个自然数,则 N=P*X (P为质数),所以输出1。
AC 代码:
#include<bits/stdc++.h>
using namespace std;
long long n;
bool prime(long long n){
if(n==1) return 0;
else if(n==2) return 1;
for(long long i=2;i*i<=n;i++){
if(n%i==0) return 0;
}
return 1;
}
int main(){
freopen("down.in","r",stdin);
freopen("down.out","w",stdout);
cin>>n;
if(prime(n)){
cout<<0;
return 0;
}
cout<<1;
fclose(stdin);
fclose(stdout);
return 0;
}
(2) 第二题:分组
情况:赛中100分。
题意:小可总共召集了n位玩家,每位玩家有一个专属分数 ,现在需要将玩家进行分组,分组之后每个组将获得一个小组专属分数 。小组专属分数为小组内每位玩家专属分数组成的集合中没有出现过的最小的自然数请输出最大的小组专属分数。
赛时本题做题想法:用桶数组t标记每一个出现的“玩家专属分数”,每次找没有出现过的最小的自然数作为本组的小组分数,累加求和输出即可
题解:同上,我的想法是使用一个桶数组标记出现过的每一个玩家分数,想要每个小组的分数最高,就要让从0开始的玩家专属分数的连续自然数数列长度最大。如:0 1 2 3 4 5 8 9 11 14这组数据,根据我的思路,可以分为两组:{0,1,2,3,4,5} {8,9,11,14},第一组分数为6,第二组分数为0,可以发现,如果0被分走了,剩下的数字无论怎么分组都是无意义的,我采用了模拟的思想,自定义函数ans() 用来构建每一个数列,也就是分组,t[i]==0表示i没有出现 或已经被分组了,所以我写了一个循环,条件是t[0]!=0. 只要0的数量不为0,就用ans函数继续构建数列,ans函数返回的数据就是本组的分数,如果t[0]==0,则剩下的分数一定为0,不需要再算将每次返回的数据累加起来,就是正确答案。
AC 代码:
#include<bits/stdc++.h>
using namespace std;
int n,x,t[1005],cnt=0,s=1;
int ans(){
for(int i=0;i<=1001;i++){
if(t[i]==0) return i;
t[i]--;
}
}
int main()
{
//freopen("group.in","r",stdin);
//freopen("group.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&x);
t[x]++;
}
while(t[0]!=0){
int ss=ans();
cnt+=ss;
}
cout<<cnt;
//fclose(stdin);
//fclose(stdout);
return 0;
}
(3) 第三题:抢夺地盘
情况:赛中10分。
题意:小可在这个游戏中控制了 个城镇分别编号为 ,每个城镇都有自己的钱数,因为城镇人口数量的不同,所以城镇都有自己的战斗力。将钱数最多的城镇放在了 位置,然后从1 到P的钱数排布是从小到大的,从P到N的钱数排布是从大到小的。如果a比b钱多,但b战斗力比a大,就会爆发冲突,小可现在可以通过调整城镇人数的方式更改城镇的战斗力,但是为了稳定性考虑,被更改的城镇越少越好,请问小可最少调整几个城镇可以满足要求?
赛时本题做题想法:比赛时读懂了题,也看出来了是DP,但没有写出代码,只能骗分(悲)
题解:1-p最长不下降序列,p-n最长不上升序列,纯纯的DP,但是10的五次方会超时,所以要用二分优化,两个数列的长度加起来 再减去就是正确答案。
AC代码:
#include<bits/stdc++.h>
using namespace std;
int n,pos,a[100005],b[100005],c[100005],p=0;
int main()
{
//freopen("seize.in","r",stdin);
//freopen("seize.out","w",stdout);
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[r]=a[i];
}
}
ans+=(n-pos-p+1);
cout<<ans;
//fclose(stdin);
//fclose(stdout);
return 0;
}
(4) 第四题:闯关
情况:赛中10分。
题意: 小可和达达两人分别在两个跑道进行闯关,两个人不能串跑道,并且只只能向前方进行闯关,不能后退,并且假设小可和达达闯的关卡都可以顺利通过但是连续闯关太 累,小可、达达可以选择一次跃过最多m距离后继续向后闯关, 不需要每个关卡都闯过去。由于小可和达达是组队参加, 组委会赠与了小可和达达一个闯关神器,可以让m 距离变成 k(m<k)。开始时神器在小可的手中,小可和达达虽然分别在两个跑道,但是可以在两人距离不超过 q (k<q)时相互传递这个闯关神器。 请问小可和达达都到达终点(即第n 个关卡),最少需要使用几次闯关神器。
赛时本题做题想法:读题后尝试用模拟,奈何我太弱,不会写代码,跟第三题一样只能骗分, 只得了10分
题解:用贪心的思路,分两种情况:1.神器在小可手上 2.神器在达达手上 第一种情况:当神器在小可手上时,先用while循环求出达达不用神器可以到达的最远距离,
while(b[Lb+1]-b[Lb]<=m&&Lb+1<=n) Lb++;
如果此时达达到达终点,则跳出循环。否则继续,达达只能到第Lb关,则小可能持神器到达的关卡距离Lb关不能超过q,否则无法传递,并且小可要尽可能的多走,传递次数才能尽可能的少,所以while循环应该这样写:
while(a[La+1]-b[Lb]<=q&&La+1<=n) La++;
第二种情况与第一种思路一致,只不过需要修改变量名,不多加赘述。两种情况写完之后就用一个flag标记神器在谁手上,再加亿点点细节即可~~~
AC 代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,k,q,a[1005],b[1005],La,Lb,ans;
int main()
{
//freopen("barrier.in","r",stdin);
//freopen("barrier.out","w",stdout);
cin>>n>>m>>k>>q;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
cin>>b[i];
}
int flag=1;
while(1){
if(flag==1){
while(b[Lb+1]-b[Lb]<=m&&Lb+1<=n) Lb++;
if(Lb==n) break;
while(a[La+1]-b[Lb]<=q&&La+1<=n) La++;
ans++;
flag=0;
if(La==n) break;
}
else{
while(a[La+1]-a[La]<=m&&La+1<=n) La++;
if(La==n) break;
while(b[Lb+1]-a[La]<=q&&Lb+1<=n) Lb++;
ans++;
flag=1;
if(Lb==n) break;
}
}
cout<<ans;
//fclose(stdin);
//fclose(stdout);
return 0;
}