Python解题 - CSDN周赛第17期 - 拯救公主

本期又出现了题目测试数据的问题,而且题目和算法关系也不太大,基本就属于用代码代替手工解答算术题的感觉,不禁让人怀疑官方题库是否已经没有高质量的题了。


第一题:判断胜负

已知两个字符串A,B。连续进行读入n次。每次读入的字符串都为A|B。输出读入次数最多的字符串。

分析

这题太简单了,但是问哥想复杂了,审题不严,漏看了题目已经限定了只有两个字符串,还引入了Counter类来生成字典,杀鸡用了牛刀。不过由于输入是字符串,用字典来计数应该是最简便的,只要按照输入顺序分别累加A或B的次数,最后再进行比较输出就好了。唯一要注意的地方就是有可能两个字符串出现的次数一样,这时要输出“dogfall”,表示平手。

参考代码

d = dict()
n = int(input().strip())
for _ in range(n):
    s = input().strip()
    d[s] = d.get(s,0) + 1
if len(d)>1 and len(set(d.values()))==1:
    print("dogfall")
else:
    print(max(d.items(),key=lambda x:x[1])[0])

第二题:买铅笔

P老师需要去商店买n支铅笔作为小朋友们参加编程比赛的礼物。她发现商店一共有 3 种包装的铅笔,不同包装内的铅笔数量有可能不同,价格也有可能不同。为了公平起见,P老师决定只买同一种包装的铅笔。商店不允许将铅笔的包装拆开,因此P老师可能需要购买超过 n 支铅笔才够给小朋友们发礼物。现在P老师想知道,在商店每种包装的数量都足够的情况下,要买够至少 n 支铅笔最少需要花费多少钱。

分析

很久之前做过的题了,也没什么可说的。x、y 分别代表了三种包装每包的铅笔数量和价格,然后用总数 n 除去每种包装的铅笔数量,向上取整(因为拆开就得全买),最后比较一下三种情况里哪种用钱最少即可。

代码也就一句话的事:

参考代码

n = int(input())
arr = [list(map(int, input().strip().split())) for _ in range(3)]
print(min((n//x+bool(n%x))*y for x,y in arr))

第三题:拯救爱情

小艺酱走到一个花之占卜店中。 店员小Q看到小艺酱可怜的样子,允许小艺酱免费占卜一次。 花瓣占卜: 1. 一瓣“在一 起”,一瓣“不在一起”;开始的花瓣表示“在一起”。 2. 直到最后一个花瓣落地游戏结束。 3. 可以选择多朵花,选择撕一朵花就必须撕完。

示例

示例一示例二
输入

1

1

6

0 8 3 5 7 7

输出127

分析

本期争议最大的题目。除了长期霸榜的某选手,其他人一律只通过70%。

从题面和给出的示例上看,最后答案一定是奇数。做法也有很多种,这里也就不啰嗦了。但让人百思不得其解的就是那30%不通过的数据到底长什么样?

参考代码

n = int(input().strip())
nums = list((map(int, input().strip().split())))
res = 0
odd = []
for i in nums:
    if i%2: 
        odd.append(i)
    res += i
if res%2==0:
    res -= min(odd)
print(res)

有人说可能是因为没有考虑到最终结果得不到奇数的情况,也就是说给的花瓣数只有偶数。但是很可惜,问哥早就测试过了。对于编译型语言可能无法做这样的测试,但是解释型语言如python很简单就可以判断测试数据里有没有特定的值。比如加个判断语句,当最终结果是偶数的时候,执行一条ValueError的代码,如 int("abc")。由于python是逐行执行,如果代码逻辑执行不到这里,就不会检查出这里的Value错误。而问哥加上这句后,代码还是只通过70%的测试用例,并不报错,说明测试用例里不存在只有偶数的情况。只能等官方解释吧(虽然基本没有)。


第四题:拯救公主

在Flower Kingdom里,住着一位美丽的公主Ana,有一天Ana得了一种怪病,神医告知国王,在遥远的幽谷中有一种药能治愈Ana, 但是神医只有一份不完整的地图,地图的描述如下: 该地图的共有3行,第一行有m列,m为奇数,第二行有m+1列,第三行有m+2列; 每一行用一个字符串表示,只有【两种字符】;‘.'表示草地,可以从它上面通过,‘*’表示岩石,每一行最多一个‘*’;入口在左上角,由于在对角线方向上,因此即使对角线两边都有岩石,但是缝隙较大,人可以通过,故人可以向八个方向行走; 真实地图是由该地图的【每一行无限循环】得到的,这种神奇的药草就生长在第x行第y列的草地上,药草可能会在岩石上;国王决定派遣勇敢的骑士David前去寻找拯救公主的解药;现在聪明的你是否知道David能否找到该药?

第一行输入T,表示测试用例的个数,第二行三个整数 m, x, y,分别代表第一行字符串的长度、草药所在地的行、列。接着三行字符串,然后循环 T 次。

输出YES或NO,针对 T 个用例依次换行输出。

示例:

示例
输入

1

3 1 10

.*.

...*

..*..

输出NO

分析

字符串题,简单概况来说就是,有三行 n 列不断重复的字符串矩阵(三行各自重复字段长度不同),矩阵中的点(.)代表草地,可以通行,星号(*)代表石头,不能通行,而只要石头不在同一列的位置,就可以斜向通行,每一行的重复字段里最多只有一个星号。问勇敢的骑士能不能到达矩阵中的指定位置。

我们画图来看看,以示例为例:

可以看到,在将每一行的字符串循环拼接后,在第8列的位置,出现了三行都是石头的情况,而草药在第10列,这个时候,勇士就无法到达草药的位置了,所以结果输出NO。

接下来,我们来仔细分析一下本题。首先,题目说了入口在左上角,换句话说,如果第一行字符串的第一个字符是星号(“*”),则不用继续判断,答案就是NO。

其次,题目还说了,草药有可能长在石头上(这不坑人么),如果出现这种情况,也不用再去判断其他行,答案也是NO。

再次,题目还说了,每行重复字符串里的星号(*)最多只出现一次,那也有可能不出现啊,如果有至少一行字符串里没有星号(“*”),而草药所在的位置不是星号(在上一步检查过了),则答案就肯定是YES了,因为如果只有两行有石头的情况下,草药必须是下面这样的位置,勇士才无法找到。而无论哪种位置,都是不可能出现的。(中间行的最小长度是2,不可能出现3个星号连在一起的情况)

如果经过上面的判断,还无法得到答案(不管YES还是NO),我们就需要进行终极判断了——找到那堵“墙”的位置,也就是三行一定存在某个相同的列,都是星号。然后再用这个列的位置和草药所在的列的位置进行对比,如果草药在前(还没遇到“墙”呢),答案就是YES,如果草药在后(比如示例),答案就是NO。

连续三个自然数(m,m+1,m+2)的最小公倍数 p 等于三者之乘积(除非m等于2,但题目说了m是奇数),也就是说,三个字符串不管长什么样,一定在第 p+1 列开始重复之前的样子。于是,我们只要找到星号在每一行的初始位置,然后以 p+1 为上界,以 m/m+1/m+2 为步长,进行枚举,找出三者相同的列数,就等于找到答案了(问哥这里用了集合里求交集的办法)。

参考代码

T = int(input().strip())
res = []
for _ in range(T):
    m, x, y = map(int, input().strip().split())
    s = [input().strip() for _ in range(3)]
    if s[0][0] == "*":
        res.append("NO") # 第一种情况,入口即岩石
    elif s[x-1][(y-1)%(m+x-1)] == "*":
        res.append("NO") # 第二种情况,草药长在石头上
    else:
        stone = []
        for i in s:
            temp = i.find("*")
            if temp < 0:
                res.append("YES") # 第三种情况,有一行没有石头
                break
            stone.append(temp)
        else:
            p = m*(m+1)*(m+2)
            wall = set(range(stone[0],p,m))&set(range(stone[1],p,m+1))&set(range(stone[2],p,m+2))
            if y-1 > wall.pop():
                res.append("NO")
            else:
                res.append("YES")
for i in res:
    print(i)

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

请叫我问哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值