本人蒟蒻一枚,帖子中如有错误或有模糊不清的地方,请在评论区指正
T1 小明上学
考点:模拟
我愿称为全场最水
分别考虑 4 4 4 种情况:
- 经过一段路段
直接将经过该路段的消耗时间累加给答案即可。
- 看见一个红灯
是人都知道,红灯我们是不能走的,红灯之后是绿灯,绿灯可以通行,所以只需要将等待红灯所需的时间(即倒计时秒数)累加给答案即可。
- 看见一个黄灯
是人都知道,黄灯我们是不能走的,黄灯之后是红灯。
所以当我们遇到黄灯时,需要将等待黄灯所需的时间(即倒计时秒数)以及红灯的总时长即可。
- 看见一个绿灯
直接冲就完事了,不消耗任何时间。
T3 循环数
考点:模拟
暴力循环能过?(还以为会超时)
因为要求第一个比 M M M 大的数,所以可以写一个从 M + 1 M+1 M+1 开始的循环。当判断出 i i i 为循环数时,输出即可。
读题:
循环数是各数位上的数互不相同且每一位非 0 0 0 的整数
所以,可以先写一个 check 函数,来判断该数是否为各数位上的数互不相同且每一位非 0 0 0 的整数。
接下来,该判断该数是否为循环数了。
首先,可以定义一个 a a a 数组, a [ 1 ] a[\ 1\ ] a[ 1 ] 存放该数的第 1 1 1 位, a [ 2 ] a[\ 2\ ] a[ 2 ] 存放该数的第 2 2 2 位,以此类推。
接下来,按照题目意思模拟即可。
结合代码:
int k=1; //记录当前下标的位置
int sum=a[1]; //本次移动的步数,即当前下标的数组数值
for(int i=1;i<=sum;i++){
k++; //移动
if(k>len){ //当大于该数的数位长度的时候,回到 1
k=1;
}
}
flag[k]=1; //当前下标已到达过,标记
while(1){
if(k==1){ //根据题意,当下标回到 1 时
for(int i=1;i<=len;i++){
if(flag[i]==0){ //只要有一个数位没有到达过,说明该数不可能为循环数
return 0;
}
}
return 1; //每一个数位都曾到达过,说明该数是循环数
}
int sum=a[k]; //移动下标,同上
for(int i=1;i<=sum;i++){
k++;
if(k>len){
k=1;
}
}
if(flag[k]==1){ //曾经到达过该位置,说明该数不是循环数
return 0;
}
flag[k]=1;
}
T5 逆序对数列
考点:DP+前缀和
这道题的状态还是很好想的.
定义状态:
d p [ i ] [ j ] dp[\ i\ ][\ j\ ] dp[ i ][ j ] 表示用 1 , 2 , ⋯ , i 1,2,\cdots,i 1,2,⋯,i 共 i i i 个自然数所能排出带有 j j j 对逆序对的排列总数。
考虑插入一个最大的数,插入该数所能产生的逆序对的数量在 [ 1 , i − 1 ] [1,i-1] [1,i−1] 个。
那么,那么,我们可以得到一个基本的状态转移方程:
d p [ i ] [ j ] = ∑ k = m i n ( 0 , i − j + 1 ) j d p [ i − 1 ] [ k ] dp[\ i\ ][\ j\ ]=\sum_{k=min(0,i-j+1)}^j dp[\ i-1\ ][\ k\ ] dp[ i ][ j ]=k=min(0,i−j+1)∑jdp[ i−1 ][ k ]
但是,这是一个 O ( n k 2 ) O(nk^2) O(nk2) ,显然不行。
所以我们就需要用到前缀和
for(int i=2;i<=n;i++){
int k=0; //前缀和准备
for(int j=0;j<=m;j++){
k+=dp[i-1][j]; //累加
k%=10000; //模预算
dp[i][j]=k; //赋值
if(j>=i-1){ //当满足该条件时,说明 dp[i][j-i+1] 不能为 dp[i][j+1] 作贡献了
k=((k-dp[i-1][j-i+1])%10000+10000)%10000; //减去,注意取模
}
}
}
这样一来,我们就实现了降维打击,时间复杂度只有
O
(
n
k
)
O(nk)
O(nk) 了。
T6 I’m stuck!
考点:BFS(好像用 DFS 也可以做)
本蒟蒻写了两个BFS,分别解决两个问题。
- 玩家是否可以从初始位置移动到终点以及玩家可以从初始位置移动到的方格位置
首先,在输入地图的时候,我们就可以记录起点以及终点的坐标,并将其改为 ‘+’ ,方便之后搜索。
根据当前所在的位置状态不同,遍历方式也有所不同。
题目也有所阐述,这里不再赘述。
那么,在完成搜索后,我们应该可以得到一个 f l a g flag flag 数组,就样例为例,我们可以得到如下的一个数组:
11111
00101
10100
11111
00001
其中,数值为 0 0 0 的格子表示不能从初始位置到达该位置,反之,则表示能从初始位置到达该位置。
在接下来求解第二个问题时,我们会再次用上它。
当然,如果到达不了终点,我们也可以撒手不管了。
- 玩家不可以从此方格移动到目标位置的格子
这里我们也要写一个 BFS,但它有些不同。
正常的 BFS 是判断当前所在位置上的格子是否能到达其四周的格子,而这个 BFS 则是判断当前所在位置上的格子的四周的格子是否能到达该格子。
下面具体举例:
- 当前所在位置上的格子可以从上方到达的
换言之:
%
@
我们需要判断出 ‘%’ 格子是否能到达 ‘@’ 格子(应该都能理解我的意思吧?)
当当前所在位置上的格子可以从下方到达,则当前格子需要满足以下条件:
- 该格子不在地图的最上方
- 该格子上方为 ‘+’ 或 ‘|’ 或 ‘.’
满足以上条件,说明当前所在位置上的格子可以从上方到达,可以压入队列,继续进行搜索。
2.当前所在位置上的格子可以从下方到达的
当当前所在位置上的格子可以从下方到达,则当前格子需要满足以下条件:
- 该格子不在地图的最下方
- 该格子下方为 ‘+’ 或 ‘|’
满足以上条件,说明当前所在位置上的格子可以从下方到达。
3.当前所在位置上的格子可以从左方到达的
当当前所在位置上的格子可以从左方到达,则当前格子需要满足以下条件:
- 该格子不在地图的最左侧
- 该格子左方为 ‘+’ 或 ‘-’
满足以上条件,说明当前所在位置上的格子可以从左方到达。
4.当前所在位置上的格子可以从右方到达的
当当前所在位置上的格子可以从右方到达,则当前格子需要满足以下条件:
- 该格子不在地图的最右侧
- 该格子上方为 ‘+’ 或 ‘-’
满足以上条件,说明当前所在位置上的格子可以从右方到达。
完成本次搜索后,我们又可以得到一个全新的 f l a g 1 flag_1 flag1 数组,以样例为例:
11111
11100
11100
11111
00000
按照题意,我们要求的是 f l a g [ i ] [ j ] = 1 flag[\ i\ ][\ j\ ]=1 flag[ i ][ j ]=1 且 f l a g 1 [ i ] [ j ] = 0 flag_1[\ i\ ][\ j\ ]=0 flag1[ i ][ j ]=0 的方格数量。统计出来后,输出即可。
T8 题海战
考点:set
第 2 2 2 ~ n + 1 n+1 n+1 行,先是 1 1 1 个正整数 p p p ,然后 p p p 个整数表示第 i i i 个学生的做题记录(可以重复做同一道题)。
从这段话中,我们可以得到两个信息:
- 做题顺序不一定是有序的
- 可能有重复做的题
所以,我们要做两个操作:排序和去重
养 set 千日,用 set 一时
首先,处理学生的题目。
考虑有多个学生,所以我们要定义一个 set 数组,即:
set<int> s[105];//set[i]表示第 i 个学生的完成的题目的升序排序后的结果
接下来,按照题目要求输入。(你不要告诉我你不会输入)
然后,处理每一场比赛或训练应该准备的题目
- 比赛
准备一个 int 类型的 f l a g flag flag 数组,其中, f l a g [ i ] flag[\ i\ ] flag[ i ] 表示有 i i i 个要参加比赛的学生学生完成了第 i i i 题。
一个两重循环即可完成该任务(第 1 1 1 重循环枚举学生,第 2 2 2 重循环枚举当前学生所完成的题目)。
亲测,不用去重,不会超时。
对于每场比赛,他要保证所出的题没有任何一道已有任何一个学生做过
所以,再用 1 1 1 重循环枚举 f l a g flag flag ,当 f l a g [ i ] = 0 flag[\ i\ ]=0 flag[ i ]=0 时,输出当前的 i i i 。
我认为我应该讲清楚了
- 训练
前面的步奏和处理比赛一模一样,但最后一步有所不同。
而对于每场训练,他要保证所出的所有题都被每一个参赛学生做过
所以,用 1 1 1 重循环枚举 f l a g flag flag ,当 f l a g [ i ] = q ( 参 赛 学 生 数 ) flag[\ i\ ]=q(参赛学生数) flag[ i ]=q(参赛学生数) 时,输出当前的 i i i 。
这题就完成了?
注意:这里有个大坑点!!!
每行的第1个整数type表示是训练或者比赛(1为训练,非1为比赛)
所以,我们应该写成:
if(type==1){
训练代码
}else{
比赛代码
}
而不是:
if(type==0){
比赛代码
}else{
训练代码
}
我就在这里卡疯了,现在我自己想给自己一巴掌
至此,结束。