CSDN第13次竞赛题解与总结

前言

2022/12/7 CSDN第13次竞赛「人民邮电出版社 & CSDN」联合主办。

本次奖品为《计算之魂》:
系统地讲解了计算机科学的精髓,不仅有助于了解计算机科学,更有助于了解 IT 产业的技术特点、培养一些特殊的思维方式、掌握信息时代特殊的做事方法。
在这里插入图片描述说实话,这本书对我这个初中未毕业的蒟蒻用处不大,我只是想拿个定制帆布包(毕竟这东西我比较缺),所以慢慢调了一个多小时,结果没想到

在这里插入图片描述既然这本书与我这么有缘,我就勉为其难的摆在书架里吃灰吧

建议

还是和上次一样的,原题太多了。
19:00我准时打开了考试,结果一看第一题——陶陶摘苹果
作为一名刷完近二十年普及组题目的蒟蒻,这不是NOIP普及组原题吗?
于是19:01爆切T1
然后一看T2,似乎在哪做过,但又想不起来。
考完后百度一查原题在这
后两题不知道是不是原题,有找到的可以评论区回复。
还有就是这可恶的360跳出一个弹窗我就随手关了一下怎么就被判切出页面。了

题解

T1陶陶摘苹果

题意

陶陶家的院子里有一棵苹果树,每到秋天树上就会结出 10 个苹果。苹果成熟的时候,陶陶就会跑去摘苹果。陶陶有个 30厘米高的板凳,当她不能直接用手摘到苹果的时候,就会踩到板凳上再试试。 现在已知 10 个苹果到地面的高度,以及陶陶把手伸直的时候能够达到的最大高度,请帮陶陶算一下她能够摘到的苹果的数目。假设她碰到苹果,苹果就会掉下来。

分析

签到题(毕竟是普及组第一题),这种题学过循环和条件分支的小学生都会做,搞不懂为什么那么多0分。

代码

#include<bits/stdc++.h>
using namespace std;
int n,ans,a[20];
int main(){
	for(int i=1;i<=10;i++) cin >> a[i];
	cin >> n; n += 30;
	for(int i=1;i<=10;i++)
		ans += a[i]<=n?1:0;
	cout << ans;
	return 0;
}

T2硬币的面值

题意

小A有n枚硬币,现在要买一样不超过m元的商品,他不想被找零,同时又不想带太多的硬币,且硬币可以重复,现在已知这n枚硬币的面值,请问最少需要多少枚硬币就能组合成所有可能(即能组合成1-m任意之间的数字)的价格?

分析

吐槽

拿洛谷月赛原题真的不会被kkk告侵权吗?
虽说这是一道原题,但最让人难堪的并不是这题有多难,而是这题不给数据范围
不过我考场上写的 O ( n m ) O(nm) O(nm)能过就说明这题并不像原题数据范围那么恶心(低情商:这题数据太水了)。
这里就用洛谷上 O ( n l o g n ) O(nlogn) O(nlogn)的算法给大家讲解, O ( n m ) O(nm) O(nm)的很好想这里就不提了。

思路

首先先排序。
先明确一点,如果不存在硬币面值为1则价格为1的就无法组成,直接特判No answer!!!
反之,只要有面值为1的,就能组成所有面额,这毕竟每个数都有因子1。

设当前可取 [ 1 , s u m ] [1,sum] [1,sum]的所有面额,且当前取了一个面额为 a a a 的硬币, a n s ans ans记录答案

容易证明,当 s u m > a − 1 sum> a-1 sum>a1时,再取这枚硬币,就能取到 [ 1 , s u m + a ] [1,sum+a] [1,sum+a]的所有面额,有兴趣的读者可以自己证明

那么我们只需将 s u m sum sum不断加 a a a,直到它大于下一枚硬币的面额,再将 a a a更新
不过这样常数比较大,有可能遇到 a i a_i ai a i + 1 a_{i+1} ai+1相差很大的情况,比如 n = 1 , a 1 = 1 n=1,a_1=1 n=1,a1=1,此时算法退化到 O ( m ) O(m) O(m),导致 a i a_i ai持续累加,所以我们可以直接用除法算出需累加几个 a a a,从而达到 O ( n ) O(n) O(n)的复杂度

最后有一种特殊情况,就是当最后跳出循环时 s u m = m − 1 sum=m-1 sum=m1,此时应特判输出 a n s + 1 ans+1 ans+1,因为还需一个1元硬币

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int a[201000];
int ans,sum;
signed main(){
	cin >> n >> m;
	for(int i=1;i<=n;i++) cin >> a[i];
	a[n+1] = m; sort(a+1,a+1+n);
	if(a[1] != 1){
		cout << "No answer!!!";
		return 0;
	}
	for(int i=1;i<=n;i++){
		if(sum < a[i+1]-1){
			int k = (a[i+1]-2-sum)/a[i]+1;
			sum += a[i]*k;
			ans += k;
			if(sum >= m){
				cout << ans;
				return 0;
			}
		}
	}
	cout << ans+1;
	return 0;
}

T3公司新表

题意

公司里为了凸显公司的特性。 安装了一个n进制表。 已知新的表的时间是”H:M”。 时间合法的定义为H<=23 &&M<=59。 时间有多少种进制定义的方式,依次打印出来。 如果有无数种解输出”-1”,不存在输出”0”。

分析

本题是一道纯模拟题,只需掌握怎么算k进制数,但细节很多,我也就调了四五十分钟
我们先考虑什么时候有无数个解,我刚开始以为只有00:00时有无数解,但实际上,因为 x 0 = 1 ( x ≠ 0 ) x^0=1(x ≠ 0) x0=1(x=0),所以k进制的末尾取值与k无关,这会导致类似于08:09,0B:0E这种也有无数个解,需特判
然后我们先找到 : : : 的位置,那么其左边就是题中的H,右边就是M,我们只需算出它们的值再判断是否符合题意即可
值得注意的是,我们还应考虑k的最小取值,举个例子12:23最小应是4进制,
0A:BC就至少是13进制,如果k小于这些值将发生近位,那么这个数就是不合法的
对于无解的情况,只需用一个变量记录是否有输出,若没有,就是无解

代码

分析完就容易实现了

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int c=2;
int main(){
	string s;
	cin >> s;
	bool flag = false;
	s = " "+s;
	unsigned int l = 0;
	for(unsigned int i=1;i<s.size();i++){
		if(s[i] == ':') l = i;
		else if(s[i] >= 'A') c = max(c,s[i]-'A'+11);
		else c = max(c,s[i]-'0'+1);
	}
	for(int i=c;i;i++){
		int base = 1,a = 0,b = 0;
		for(unsigned int j=l-1;j>=1;j--){
			if(isdigit(s[j])) a += base*(s[j]-'0');
			else a += base*(s[j]-'A'+10);
			base *= i;
		}
		base = 1;
		for(unsigned int j=s.size()-1;j>=l+1;j--){
			if(isdigit(s[j])) b += base*(s[j]-'0');
			else b += base*(s[j]-'A'+10);
			base *= i;
		}
		if(a<c && b<c && a<=23 && b <= 59){
		//注意当a>=24或b>=60时应判为无解
			cout << -1;
			return 0;
		}
		if(a > 23 || b > 59) break;
		flag = true; cout << i << " ";
	}
	if(!flag) cout << 0;
	return 0;
}

T4小豚鼠排排坐

题意

小艺酱买了一个由一排排格子组成的小房子nm,她想让k个小豚鼠每个小豚鼠都有自己的格子。
但是为了不浪费空间,她想要最边角的一圈2
(n+m-2)每行每列的格子都有一个小豚鼠居住。
具体来说,假设这k只小豚鼠的格子坐标为(x1, y1), (x2, y2),…,(xk, yk),则需要满足存在1<a,b,c,d<=k,使得xa =1, xb = n, yc = 1, yd = m (a, b, c, d可以重复).
小艺酱想知道自己有多少种方案安排小豚鼠。

分析

这题的题意有点乱第二段话我也看不是很懂
但一看数据范围 n , m ≤ 5 n,m\le5 n,m5这不就是一道 d f s dfs dfs模板题吗
对于每一个格子,要么选,要么不选,那我们用搜索枚举所有情况,再逐一判断是否成立,时间复杂度 O ( 2 n m ) O(2^{nm}) O(2nm)

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m,k,ans;
int a[10][10];
bool chk(){//四个条件直接枚举判断
	bool flag = 0;
	for(int i=1;i<=n;i++)
		if(a[i][1] == 1){
			flag = 1;
			break;
		}
	if(!flag) return false;
	flag = 0;
	for(int i=1;i<=n;i++)
		if(a[i][m] == 1){
			flag = 1;
			break;
		}
	if(!flag) return false;
	flag = 0;
	for(int i=1;i<=m;i++)
		if(a[1][i] == 1){
			flag = 1;
			break;
		}
	if(!flag) return false;
	flag = 0;
	for(int i=1;i<=m;i++)
		if(a[n][i] == 1){
			flag = 1;
			break;
		}
	if(!flag) return false;
	return true;
}
void dfs(int x,int y,int z){
	if(z == k){
		ans += chk();
		return ;
	}
	if(x == n+1) y++,x=1;
	if(y == m+1) return;
	dfs(x+1,y,z);
	a[x][y] = 1;
	dfs(x+1,y,z+1);
}
int main(){
	cin >> n >> m >> k;
	dfs(1,1,0);
	cout << ans;
	return 0;
}
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值