CSP-J模拟赛一张之轩补题报告

日期:2023-09-30 周六
学号:S12358 

一:
总分数:170
T1【数字降级(down)】:40
T2【分组(group)】:100
T3【抢夺地盘(seize)】:20
T4【闯关(barrier)】:10

二:比赛过程
比赛中我先大体看了一下四道题,然后从第一题开始做,可理解错误题目意思,只得了40分。
然后是第二题,我只看了一遍题目,就用暴力算出,只用了20分钟得了100分。
之后是第三题,我第一次看以为很简单,就用了二十几行代码写完,可落入陷阱,没考虑全面,得了20分。
最后我去看第四题,再考虑30分钟后,我自以为发现了一种简单思路,但只有10分。(应该用DP)

三:题目分析
T1【数字降级(down)】
1、题目大意
数字每一次降级都表示将一个数字除以一次它的任意一个因子。
求最少几次操作可以将一个数字 n ,降级成一个质数。
2、比赛中的思考
8的因子应为1,2,4,8,我却理解为8=2*2*2,纯属理解错误。
3、解题思路
其实这道题只有两种情况:1/0.
当答案为1时:n为非质数;
当答案为0时:n为质数。
所以仅需判断n是否为质数即可。
4、AC代码

#include
#include
#include
#include
#include
using namespace std;
long long n;
int main() {
	cin>>n;
	for(int i=2;i<=n/i;i++){
		if(n%i==0){
			cout<<"1";
			return 0;
		}
	}
	cout<<"0";
	return 0;
}

T2【分组(group)】
1、题目大意
小组专属分数为小组内每位玩家专属分数组成的集合中没有出现过的最小的自然数。
输出最大的小组专属分数之和。
2、比赛中的思考
看了一分钟就有了思路(暴力强算)
事实证明可行
3、解题思路
小组中0的出现至关重要,如果一个组中没有0,那它的分数就是0。
统计每个数字出现的次数,循环判断,如果当前的数的次数比下一个数的次数大,那么ans=ans+(i*(b[i-1]-b[i]));(累加)否则为了程序后面正常运行将b[i]赋值为b[i-1];
4、AC代码

#include
#include
#include
using namespace std;
int n,a[100005],b[100005],ans;
int main() {
	//freopen("group.in","r",stdin);
	//freopen("group.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		b[a[i]]++;
	}
	sort(a+1,a+n+1);
	if(b[0]==0){
		cout<<"0";
		return 0;
	}
	for(int i=1;i<=1005;i++){
		if(b[i]b[i-1]){
			b[i]=b[i-1];
		}
	}
	cout<

T3【抢夺地盘(seize)】
1、题目大意
将钱数最多的城镇放在了 p 位置,然后从 1 到 p 的钱数排布是从小到大的,从p到 n 的钱数排布是从大到小的。
如果某一个城镇 a 由于钱的原因比另一个城镇 b 更靠边(距离 pp 位置更远),但是战斗力 a 比 b 更大,两个城镇会爆发矛盾。
通过调整城镇人数的方式更改城镇的战斗力,但是为了稳定性考虑,被更改的城镇越少越好,求小可最少调整几个城镇可以满足要求。
2、比赛中的思考
想到p的左边为递增,右边为递减。
然后重复判断如果有不满足以上情况就cnt++。
3、解题思路
用到DP,本质上求最长上升子序列,但时间复杂度会很高,于是使用
if(a[i]>=b[cnt1]){
    cnt1++;
    b[cnt1]=a[i];
}
并使用二分减小时间复杂度。
4、AC代码

#include 
#include
using namespace std;
long long n,a[100005],b[100005],cnt,ans;
int main() {
	//freopen("down.in","r",stdin);
	//freopen("down.out","w",stdout);
	cin>>n;
	if(n%2==0){
		cout<<"1";
		return 0;
	}
	long long nn=n;
	for(int i=2;i<=n;i++){
		if(b[i]==0){
			b[i]=1;
		}
		for(int j=i*2;j<=n;j++){
			b[i]=2;
		}
	}
	if(b[nn]==1){
		cout<<"0";
		return 0;
	}
	for(int i=2;i<=100000;i++){
		if(n==1){
			break;
		}
		if(n%i==0){
			a[++cnt]=i;
		}
		while(n%i==0){
			n/=i;
		}
	}
	if(n>1){
		a[++cnt]=n;
	}
	for(int i=cnt;i>=1;i--){
		ans++;
		if(b[nn/a[i]]==1){
			break;
		}
		nn/=a[i];
		
	}
	cout<

T4【闯关(barrier)】
1、题目大意
小可、达达可以选择一次跃过最多 m 距离继续向后闯关,不需要每个关卡都闯过去。
由于小可和达达是组队参加,组委会赠与了小可和达达一个闯关神器,可以让 m 距离变成 k(m 开始时神器在小可的手中,两人可以在距离不超过 q(k 问小可和达达都到达终点(即第 n 个关卡),最少需要使用几次闯关神器。
2、比赛中的思考
这道题我想了30分钟,然后觉得毫无思路。
只能将一些特殊情况写上,然后我也不知道怎么回事写了一个奇怪的代码,得了10分。
3、解题思路
使用DP
建立三维数组f[小可关卡数][达达关卡数][神奇谁拿],将初始值赋值为
f[0][0][0]=0;//神器初始在小可身上
f[0][0][1]=1;//初始交换
三层循环遍历神奇是否要交换。
然后:能走就走,否则就交换,两人距离超限也交换,在结果上取最小值。
4、AC代码

#include
#include
#include
#include
#include
using namespace std;
const int N=1010;
int a[N],b[N],f[N][N][2];
int main() {
//	freopen("xx.in","r",stdin);
//	freopen("xx.out","w",stdout);
	int n,m,k,p;
	cin>>n>>m>>k>>p;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=n;i++){
		cin>>b[i];
	}
	memset(f,0x3f,sizeof f);
	f[0][0][0]=0;//神器初始在小可身上
	f[0][0][1]=1;//初始交换
	for(int step=0;step<=2*n;step++){
		//step为小可和达达总关卡数
		for(int j=0;j<=min(n,step);j++){
			//j控制达达走的关卡
			int i=step-j;//小可的关卡 
			if(i>n){
				continue;//小可不能走超过n关 
			}
			for(int k=0,r=0;r<=2;r++,k++){//枚举第三维度 
				k=r%2;
				if(abs(a[i]-b[j])<=p){
					f[i][j][k^1]=min(f[i][j][k^1],f[i][j][k]+1);
					//i,j在0/1情况下,交换次数+1或者当前最小值 
				}
				if(k==0){
					f[i+1][j][k]=min(f[i+1][j][k],f[i][j][k]);
					//小可到i+1关,使用次数是第i+1关和第i关神器在小可手里使用次数的最小值
					if(b[j+1]-b[j]<=m){
						//如果达达第j关和j+1关的差值比m小,可以直接过
						f[i][j+1][k]=min(f[i][j][k],f[i][j+1][k]); 
					} 
				}else{//道具在达达手里 
					f[i][j+1][k]=min(f[i][j+1][k],f[i][j][k]);
					//达达闯关
					if(a[i+1]-a[i]<=m){//小可可以直接闯关 
						f[i+1][j][k]=min(f[i+1][j][k],f[i][j][k]);
					} 
				}
			} 
		} 
	} 
	cout<

你可能感兴趣的:(算法,动态规划)