晋城一中信息学奥赛3月月赛题解
2019-03-24 12:33
"成绩"题解 第一题,2017NOIP普及组第一题,赤裸裸的打卡题。 让我们先看一下原题。
读完题后,我们发现出奇的水。A,B,C都是10的倍数,也就是说不存在小数,也就不用考虑float和double的问题。
×20% ×30%
×50% 也就是 ×0.2 ×0.3
×0.5 -->会用到强制类型转换
×20/100 ×30/100 ×50/100
-->不会用到强制类型转换 接着输出就可以了。 代码如下: 这道题帅气的结束了呢! "ISBN号码"题解
第二题也没有想象的那么难,其实也只是一个计算加比对的过程,但是这道题细节决定成败! 先看原题:
读完题后,发现在一个ISBN号码中,一共有9个需要计算的数字,1个需要判断的数字。这道题的重点在于怎样求出正确的识别码。
那我们只需要把他们一个一个乘起来不就结束了? 也就是: for i = 0 -> 10
[ 注意数组下标从0开始 ] 识别码 += ( ( int )( a [ i ]
- '0' ) * ( ++ tot ) ) /* 其中( int )( a [ i ] - '0' )是将一个字符转换为一个数,
还是手动强制类型转换比较好,免得出现一些不可
描述的错误。 tot 是用来从1乘到9的,因为其中有符号 ' - ' 所以循
环时不能使用 i 去乘。*/ 最后 识别码%=11 即可。
- - - - - - - - - - / 手 动 分 割 线 / - - - - - - - - - -
代码如下: 第二题也华丽的结束啦! "火柴棒等式"题解 这道题也非常简单,但是细节方面需要注意! [ PS:我是第一个AC的哦! ]
我们先来看原题: 看起来无从下手,其实只需要暴力,再加点技巧即可。 加号和等于号各需要2根火柴棒,所以我们再输入n后,要
n-=4; 这样,我们只需要模拟数字即可。 - - - - - / THE FIRST QUESTION / - - -
- - 那怎么去暴力枚举数字呢? FOR !!!
两层for循环。因为最多会有24根火柴棒,所以最多能拼成1111+111=111 我们只需要模拟到1111即可,这样做的复杂度是 O(
1111^2 ) 绝对不会超时。 - - - - - / THE SECOND QUESTION / - - -
- - 为什么是两层for循环呢? 假如说A+B=C,我们只需要模拟A和B,再加出C,判断火柴棒是否用完就可以了。 for
i = 0 -> 1111 for j = 0 -> 1111
k=i+j if ( i所用的火柴棒 +
j所用的火柴棒 + k所用的火柴棒 == n /* 这时的n已经减去加号和等于号了 */ )
ans++ 核心代码写完了,剩下的就是怎么求i,j,k所用的火柴棒和输出了。
- - - - - / THE THIRD QUESTION / - - - - - 如何求i,j,k所用的火柴棒?
首先,我们要预处理一下,说白了就是打表,打出0-9数字需要多少火柴棒。 c[10] = { 6 , 2 , 5 , 5 , 4 , 5
, 6 , 3 , 7 , 6 } 那么,单个数字sum所对应的火柴棒就是c [ sum ] 我们只需要将一位一位的数字求出来即可。
/* 设i是我们需要分解的数 */ int ans = 0 ; while ( i > 0 ){
ans += c [ i %10 ]
i /= 10 } 以下是代码: "采药"题解
这道题,其实还没有上一道题难,赤裸裸的01背包问题,没有任何难点,是模板。 先看原题: 这道题我不讲,只作分析和AC代码。
在题中背包总容量是 采药的时间 在题中n件物品的费用是 采每株药所需的时间 在题中n件物品的价值是
每株药的价值 递推式 f [ j ] = max ( f [ j ] , f [ j - w [ i ] ]
+ c [ i ] ) 你要是不会01背包,请查看:
https://blog.csdn.net/weixin_39059738/article/details/79924049
注:此处为转载,版权属于原作者,一切利益归原作者所有。 以下是代码: "八数码问题"题解
啊,我先来吐波槽,一个裸的bfs题,我竟然没做出来?! 后续因为要写题解再做的时候,一遍AC。。。 先让我来炫耀一波:
以下是原题,唯一和比赛不同的是没有了-1的判断和输入处理比较麻烦。之后,我会讲洛谷上这道题。最后,有差异的地方,我会提醒大家一下。
我在看完之后一脸懵X ( 小朋们,不能说脏话 ) ,甚至尝试用DP做,我都不知道我怎么想的。。。 好,我们正式开始讲解!
这道题,没必要用什么康拓压缩,虽然它也很简单。就是一个BFS,搜到目标状态就结束,输出答案。那么,这道题就做完了。
细节--1:关于存储 我们没必要用什么一个变量去存图【康拓压缩】,我们每次也可以传一张地图过去。 struct qwe{
int x0 , y0; //0的坐标
int a [ 4 ] [ 4 ] ; //存地图
int ans ; //存已经换了多少次,即答案 } bfs队列可以用:
queue < qwe > q; //定义一个qwe型的队列 存储路径可以用map:
map < int , bool > m; //具体用法,后面会详细讲
细节--2:关于路径记录 那我们怎样判断某一状态是否出现过了呢?我们肯定不能用一个 a[][][][][][][][][]
去存储(但是好像也是可行的),得想出一个高效率的方法,用map! 也就是一一对应的方法。举个栗子就明白了:
若我们想把 1 3 4 5 0 2 6 7 8 标记为已经走过 就可以这样写: m [ 134502678 /* 把9个数合到一起
*/ ] = true 查询就是 if ( m [ 134502678 ] == false ) { ... }
这样,我们就完成了路径记录!【解决了核心问题!】 细节--3:路径记录的延伸问题
既然要把9个数变成1个数,那么一定要有个算法,我们用的是二维数组,那怎么办呢? for(long long
i=1;i<=3;i++){ for(long long
j=1;j<=3;j++){
ans += ( av.a[ i ][
j ] *pow( 10 , ( ( i - 1 ) * 3 + j ) ) ); }
} PS:不要把av想歪了!av的意思是:all_values 在其中( ( i - 1 ) * 3
)求出是在第几阶也就是0,3,6中的一阶 然后再加上 j 。 不明白的人可以带一组值。 比如
av.a[2][3]=5 那么就是:( 2 - 1 ) * 3 + 3 = 6 它的实际位置是: * * * | * * 5 | *
* * 细节--4:状态复制问题
我们在while中,会弹出队首。如果我们每次都修改这个弹出值,会涉及到回溯问题,而这个题回溯起来会比较麻烦,所以我们把弹出值复制到
av 中,无论怎么修改 av 最后只需要再用弹出值覆盖一次即可。 我们要复制一个qwe到另一个qwe中。
av.x0=b.x0;av.y0=b.y0; //复制'0'的坐标 av.ans=b.ans;
//复制答案 for(long long i=1;i<=3;i++){
for(long long j=1;j<=3;j++){
av.a[i][j]=b.a[i][j];
//复制地图 } } 细节--5:预处理
我们在移动'0'时,涉及到x,y的变化,所以,要做一个预处理,在最预处理的时候,数组名千万不要手残打成next,天哪,next是一个关键字,天知道上次月赛我被这玩意坑成啥样。做预处理时,要细心,别算错了,不然整个程序就崩了。
const long long ne[4][2]={{0,1},{1,0},{-1,0},{0,-1}};
ne[i][0]指的是x的变化 ne[i][1]指的是y的变化 细节--6:输入【在原题中是3*3矩阵,所以不考虑】
它输入的是一个无空格的字符串。 所以怎样把这个字符串转换为我们所需要的3*3数组,也是一个难题。 for(long long
i=0;i<(long long)(strlen(in_data));i++){
long long x,y;
av.a[x=(long long)(i/3)+1][y=i%3+1]=(long
long) (in_data[i]-'0'); //坐标求法上面已经讲过,逆用即可
if(av.a[x][y]==0){
//发现'0'的位置 av.x0=x;
av.y0=y;
} } 还有一个重点,strlen()的返回类型是 unsigned
int,而我们的i是 int ,所以注意强制类型转换。 细节--7:原题中的无解情况 只需要判断
队头的ans是否>5000即可 -----------/分割线/----------- 最后放上超长代码!
--end--
声明:本文章由网友投稿作为教育分享用途,如有侵权原作者可通过邮件及时和我们联系删除:freemanzk@qq.com