前言
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>a−1时,再取这枚硬币,就能取到 [ 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=m−1,此时应特判输出 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,m≤5这不就是一道
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;
}