日期:2023年10月2日星期一
S10698
1.比赛概况
比赛共有4题,满分400分,赛时爆零(文件夹名称没错的话,可以得到80分,第一题50分,第二题0分,第三题10分,第四题20分)。
2.比赛过程
先浏览了一遍题目,感觉第一题最简单,然后开始做第一题,发现第一题跟分解质因数有关,但是把问题想复杂了,用了一个小时,只拿到了50分。又开始做第三题,没有想到是LIS,用的暴力,只得了10分。做第四题只写出来一种情况得到了20分。最后时间不够了第二题基本上没写(以上得分均为改完赛时文件夹名称后得分)。
3.题解报告
(1)第一题:数字降级(down)
情况:赛中0分(文件夹名称没错的话是50分),已补题。
题意:给我们一个数n(n≥2),判断是否为质数,如果是就输出0,不是就输出1。
赛时本题做题想法:做题时发现是分解质因数类的问题。当时以为合数不止除以一次因数就能变成质数,就暴力枚举了这个合数的所有因数,每次除以最大的因数,判断结果是不是素数(我用的是最傻的方法,只判断是否是1-120的素数,没有考虑到别的可能)。
题解:判断的数n(n≥2)是否为质数,如果是就输出0,不是就输出1。
AC代码:
#include <iostream>
#include <cstdio>
using namespace std;
long long n;
int prime(long long n)
{
for(long long i=2;i*i<=n;i++)
{
if(n%i==0)
{
printf("1");
return 0;
}
}
printf("0");
}
int main()
{
scanf("%lld",&n);
prime(n);
return 0;
}
(2) 第二题:分组(group)
情况:赛中0分,已补题。
题意:在输入的n个数中找一个组成的集合中没有出现过的最小的自然数,比如说在集合{1、2、3}中,未出现过的最小自然数就是0,在集合{0、1、2}中,未出现过的最小自然数就是3,然后求所有集合中未出现过的最小自然数的最大值。
赛时本题做题想法:当时没有弄清楚到底要分成几组,所以就先跳过去了,后来时间不够就没咋写了。
题解:在输入的时候进行桶标记,然后从一到一千进行遍历,寻得最大值,累加最大值最后输出。
AC代码:
#include <iostream>
#include <cstdio>
using namespace std;
const int N=1e5+10;
int a,t[N];
int main()
{
int n;
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&a);
t[a]++;
}
int now=t[0],ans=now;
for (int i=1;i<=1000;i++)
{
now=min(now,t[i]);
ans+=now;
}
printf("%d",ans);
return 0;
}
(3)第三题:抢夺地盘(seize)
情况:赛中0分(文件夹名称没错的是10分),只完成了暴力LIS。
题意:小可根据前一部分从小到大,后一部分从大到小进行排序,使得战斗力符合要求。
赛时本题做题想法:当时没有想到是LIS,就用了暴力比较两个相邻元素之间的大小关系,在1-p时,如果后一个元素小于前一个元素,那么就将前一个元素的值赋值给后一个元素;在p-n时,如果前一个元素小于后一个元素,那么就将后一个元素的值赋值给前一个元素。
题解:求出LIS,用总长度减去LIS。
AC代码(暴力LIS未得全分):
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int N=1e3+10;
int t[N],f[N],f2[N];
int n,ret;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&t[i]);
}
for(int i=1;i<=n;i++)
{
f[i]=1;
for(int j=1;j<i;j++)
{
if(t[i]>t[j])
{
f[i]=max(f[i],f[j]+1);
}
}
}
for(int i=n;i>0;i--)
{
f2[i]=1;
for(int j=n;j>i;j--)
{
if(t[i]>t[j])
{
f2[i]=max(f2[i],f2[j]+1);
}
}
}
for(int i=1;i<=n;i++)
{
ret=max(ret,f[i]+f2[i]-1);
}
printf("%d",n-ret);
return 0;
}
(4)第四题:闯关(barrier)
总结一下就是小可和达达参加《向前冲!冲!冲!》,然后有他们要参加的关卡数和每次可以移动的距离以及借助神器可以移动的距离,神器最开始在小可手里,让我们计算它们传递几次神器才可以都到达重点。
赛时本题做题想法:没有想到贪心或者DP的想法,就只用了暴力对移动距离小于m或小于k的情况,对别的情况没有考虑。
题解:首先我们保证,神器一定在关卡位置靠后的人手里,我们设小可为位置靠后,达达位置靠前,并设小可编号为i,达达编号为j,x为第i人的位置,则有i<j。此时神器在i手中,i在使用完神器之后就将神器跑到最远的能够将神器归还给j的位置时,一定要将神器归还给j,因为此时j也需要使用神器,因为j也停留在需要使用神器的情况下,否则有j以到达终点。当i将神器归还给j后,随即跑到下一个需要神器的地方。
AC代码:
#include <iostream>
using namespace std;
#define ll = long long;
const int N = 1010;
int n, m, k, q, la, lb, ans;
bool flag = true;
int a[N], b[N];
int main() {
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];
for (;;) {
if (flag) {
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++;
if (la == n) {
while (b[lb + 1] - b[lb] <= m && lb + 1 <= n)
lb++;
if (lb < n)
ans++;
break;
}
ans++;
flag = false;
} 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++;
if (lb == n) {
while (a[la + 1] - a[la] <= m && la + 1 <= n)
la++;
if (la < n)
ans++;
break;
}
ans++;
flag = true;
}
}
cout << ans << endl;
return 0;
}
4.赛后总结:
1. 对题目的理解不够透彻,没有考虑到运用什么算法。
2. 对时间的分配不够合理,在一些题目上用的时间太长了。