CSP-J复赛 模拟题一补题报告

CSP-J复赛 模拟题一补题报告

2023.10.1 Sat.
S10473吴启瀚

1. 比赛报告

共4题, 第1题AC, 第2题40分, 第3题20分, 第4题TLE

2. 比赛过程

第一题数字降级, 思路很好想, 直接AC
第二题分组,我的思路是用桶记录,如果后面比前面大就把后面赋值成前面的,从后往前遍历,如果不为b[0] > b[i]就让b[0] - b[i], ans += b[i] * (i + 1), 漏了前面的也都要减b[i]
第三题抢夺地盘, 我的思路是贪心, 找1~p-1下降个数, p+1~n上升个数
第四题闯关, 我用贪心来做, 思路不同在于: 我按人依次考虑, 答案分神器在谁手里考虑

3. 题解

3.1 数字降级 down

题目大意: 将一个数字除以它的因子, 最少几次变成一个质数

题目解析: 如果是质数明显答案是0, 如果不是质数应该除以最大的因子例如180 = 22335, 180 / 90(233*5) = 2, 这样就会变成一个质数, 答案为1, 其实是判断素数的题
AC代码:

#include <iostream>
#include <cstdio>
using namespace std;
long long n;
bool f = 0;
int main(){
	freopen("down.in", "r", stdin);
	freopen("down.out", "w", stdout);
	
	scanf("%lld", &n);
	for(long long i = 2;i*i <= n;i++){
		if(n % i == 0){
			f = 1;
			break;
		}
	}
	if(f)	printf("1\n");
	else	printf("0\n");
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}

3.2 分组 group

题目大意: 将n个数分组, 每组分数为没出现过最小的自然数, 求最大分数和

题目解析: 考虑有多少个小组分数可以至少为1, 这些小组中必须有一个0。
所以假设0有x个, 那么就说明有x个小组分数至少为1, 此时答案增加x。
再考虑有多少个小组分数可以至少为2这些小组必须有一个1, 并且小组分数至少为2的小组的数量一定小于等于小组分数至少为1的小组数量, 所以要有求 min 的操作。
AC代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5, M = 1005;
int n, t, b[M], mn, ans;
int main(){
	freopen("group.in", "r", stdin);
	freopen("group.out", "w", stdout);
	
	scanf("%d", &n);
	for(int i = 1;i <= n;i++){
		scanf("%d", &t);
		b[t]++;
	}
	mn = b[0];	ans = mn;
	for(int i = 1;i <= 1000;i++){
		if(b[i] < mn)	mn = b[i];
		ans += mn;
	}
	printf("%d\n", ans);
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}

3.3 抢夺地盘 seize

题目大意: 有一条城堡, 城堡有钱和战斗力两个值, 给定p位置, 从1到p钱从小到大, 从p到n从大到小, 战斗力比更靠p的高会找事, 可以调整任意城堡的战斗力, 求最少移动几次

题目解析: 最长上升子序列问题, 从1到p求一次最长不下降子序列长度,然后从p到n求一边最长不上升子序列长度
AC代码:

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 1e5 + 5;
int n, p, a[N], ans, dp[N], len;
int main(){
	freopen("seize.in", "r", stdin);
	freopen("seize.out", "w", stdout);
		
	scanf("%d %d", &n, &p);
	for(int i = 1;i <= n;i++)
		scanf("%d", &a[i]);
	
	dp[++len] = a[1];
	for(int i = 2;i < p;i++){
		if(a[i] >= dp[len])	dp[++len] = a[i];
		else{
			int l = 1, r = len, m = len >> 1;
			while(l != r){
				if(a[i] < dp[m])	r = m;
				else	l = m + 1;
				m = l + r >> 1;
			}
			dp[l] = a[i];
		}
	}
	if(a[p] >= dp[len])	len++;
	else	a[p] = 0x3f3f3f3f;
	ans = p - len;
	
	len = 1;
	memset(dp, 0, sizeof(dp));
	dp[len] = a[p];
	for(int i = p + 1;i <= n;i++){
		if(a[i] <= dp[len])	dp[++len] = a[i];
		else{
			int l = 1, r = len, m = len >> 1;
			while(l != r){
				if(a[i] > dp[m])	r = m;
				else	l = m + 1;
				m = l + r >> 1;
			}
			dp[l] = a[i];
		}
	}
	ans += n - p - len + 1;
	printf("%d\n", ans);
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}

3.4 闯关 barrier

题目大意: 两个人AB有n个关卡要过, 两个关卡之间有距离, 一次可以过m距离, 有一个神器一开始在A手上, 拿着神器的人可以一次过k距离, 在两人距离在q之内时可以传递

题目解析:

3.4.1 思路1 贪心

设A在前B在后, 神器在B手中, 要贪到可以回传的最远距离, 如果能到达终点, 输出传的次数+1(因为要传给B), 反之A在后B在前同理
AC代码:

#include <iostream>
#include <cstdio>
using namespace std;
const int N = 1005;
int n, m, k, q, a[N], b[N], ans, x, y;
bool f;
int main(){
	freopen("barrier.in", "r", stdin);
	freopen("barrier.out", "w", stdout);

	scanf("%d %d %d %d", &n, &m, &k, &q);
	for(int i = 1;i <= n;i++)
		scanf("%d", &a[i]);
	for(int i = 1;i <= n;i++)
		scanf("%d", &b[i]);
	
	while(1){
		if(!f){
			while(b[y+1] - b[y] <= m && y + 1 <= n)	y++;
			if(y == n)	break;
			while(a[x+1] - b[y] <= q && x + 1 <= n)	x++;
			ans++;
			f = 1;
			if(x == n)	break;
		}
		else{
			while(a[x+1] - a[x] <= m && x + 1 <= n)	x++;
			if(x == n)	break;
			while(b[y+1] - a[x] <= q && y + 1 <= n)	y++;
			ans++;
			f = 0;
			if(y == n)	break;
		}
	}
	printf("%d\n", ans);
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}

3.4.2 思路2 dp

f[i][j][0/1]表示A在第i个关卡, B在第j个关卡, 0表神器在A手上, 有4种情况: A过一关, B过一关, 传递神器, 因为这一题求最小值, 多次求不会出错

4. 反思

第2题考虑不周, 想到哪写哪, 第3题没看出是道动态规划, 第4题思路有些误区, 提交时文件名不规范, 应该提高思维能力和仔细程度


本文为可达鸭2023CSP-J复赛冲刺模拟赛一补题
祝大家国庆快乐!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值