Python解题 - CSDN周赛第16期 - 流水还是积水?

问哥在最后30分钟才有空进入比赛,发现满分选手只有两位,就知道大家又遇到坑了。。。其中两道题的数据都多少有点问题。不得不佩服那两位在这种情况下还能拿满分的选手,等到他们分享解题报告后,问哥再来更新代码吧。


第一题:鬼画符门莲台争夺战

鬼画符门莲台争夺战!虽然鬼画符门是一个三流门派,但是近期为了改善宗门弟子质量,特意引进了进化莲台。部分精英弟子会自己独占一块区域,或者几个精英弟子一块占领一块区域,他们占领的区域普通弟子不可以再占领。小艺作为普通弟子想知道自己还能占领哪些地方。莲台区域以1开始,由小到大编号表示。

输入:第一行两个整数n、m,分别代表弟子数量和莲台数量(1到m),下面n行表示每个弟子占领的莲台左、右莲台编号。

输出:如果无可用莲台,输出0,否则,输出可用莲台数量,然后在第二行输出莲台编号。

分析

不记得示例了,不过不影响,反正也是水题。题意翻译过来就是,有连续整数组成的集合(1到m),删除n次数据,问剩下元素数量及内容。使用python的集合操作即可。

参考代码

n, m = map(int, input().strip().split())
total = set(range(1,m+1))
for _ in range(n):
    l, r = map(int, input().strip().split())
    total.difference_update(set(range(l, r+1)))
if len(total)==0:
    print(0)
else:
    print(len(total))
    print(*total)

第二题:津津的储蓄计划

津津的零花钱一直都是自己管理。每个月的月初妈妈给津津 300 元钱,津津会预算这个月的花销,并且总能做到实际花销和预算的相同。为了让津津学习如何储蓄,妈妈提出,津津可以随时把整百的钱存在她那里,到了年末她会加上 20% 还给津津。 因此津津制定了一个储蓄计划:每个月的月初,在得到妈妈给的零花钱后,如果她预计到这个月的月末手中还会有多于 100 元或恰好 100 元,她就会把整百的钱存在妈妈那里,剩余的钱留在自己手中。例如 11 月初津津手中还有 83 元,妈妈给了津津 300 元。津津预计 11 月的花销是 180 元,那么她就会在妈妈那里存 200 元,自己留下 183 元。到了 11 月月末,津津手中会剩下 3 元钱。 津津发现这个储蓄计划的主要风险是,存在妈妈那里的钱在年末之前不能取出。有可能在某个月的月初,津津手中的钱加上这个月妈妈给的钱,不够这个月的原定预算。如果出现这种情况,津津将不得不在这个月省吃俭用,压缩预算。 现在请你根据2004年1月到12月每个月津津的预算,判断会不会出现这种情况。如果不会,计算到2004年年末,妈妈将津津平常存的钱加上20%还给津津之后,津津手中会有多少钱。

示例1示例2
输入290
230
280
200
300
170
340
50 
90 
80 
200
60 
290 
230 
280 
200 
300 
170 
330 
50 
90 
80 
200 
60 
输出-71580

分析

本来没啥好分析的,这不就是初中数学应用题嘛,按照题目描述的一句句写代码即可,而且原题在此。但是——上次问哥还说CSDN的数据测试可能不如洛谷的严格,这么快就被打脸——同样的代码在洛谷顺利pass,在CSDN的周赛却只能通过一半的测试数据。思考许久仍找不到到底miss在哪里,等到满分同学分享代码后问哥再来更新吧。

参考代码

budgets = [int(input()) for _ in range(12)]
start = saving = 0
for i in range(12):
    start += 300 - budgets[i]
    if start < 0:
        print(0-i-1)
        break
    else:
        saving += start//100
        start %= 100
else:
    print(saving*120+start)

第三题:多边形的面积

给出一个简单多边形(没有缺口),它的边要么是垂直的,要么是水平的。要求计算多边形的面积。 多边形被放置在一个 XY 的卡笛尔平面上,它所有的边都平行于两条坐标轴之一。然后按逆时针方向给出各顶点的坐标值。所有的坐标值都是整数(因此多边形的面积也为整数)。

分析

纯纯的数学题,属于那种“知道公式很快就能写出来”的题目,和代码基本没什么关系。

n个顶点组成的多边形面积计算公式为:

\frac{abs(x_{1}*y_{2}-y_{1}*x_{2}+x_{2}*y_{3}-y_{2}*x_{3}+...+x_{n}*y_{1}-y_{n}*x_{1})}{2}

由于横纵坐标交叉相乘再相减,又可以形象地称之为“鞋带公式”,保证你看了就记住了。

参考代码

n = int(input())
vector = []
for _ in range(n):
    vector.append([int(i) for i in input().strip().split()])

a = sum(vector[i][0]*vector[i+1][1] for i in range(n-1))
b = sum(vector[i][1]*vector[i+1][0] for i in range(n-1))
a += vector[0][1]*vector[-1][0]
b += vector[0][0]*vector[-1][1]
print(abs(a-b)//2)

第四题:小桥流水人家

在n*m的地图上,存在一个喷水点,如果相邻的位置低于有水的地方,水就能流到相邻的某位置(即该格子需要其上下左右的格子海拔都比自身高的封闭图形才可以积水)。已知各个格子的海拔高度,求积水的最大覆盖个格子数。

示例:

示例1示例2
输入

3 6

3 3 4 4 4 2

3 1 3 2 1 4

7 3 1 6 4 1

3 4

3 3 4 5

3 1 2 2

9 3 1 6

输出51

分析

本期最坑的题目来了。其实这也是每日一练里出现过的题目,但是,题目组做了点修改(问哥认为是很自以为是的修改),删去了喷水点的坐标,但却并未修改题干,也未增加对示例的说明,导致大家对题意各种猜测。问哥先给出之前每日一练中使用的解法,然后再讨论一下题目组的修改意图。

在修改之前,这是一道经典的深搜题,大家在其他网站或多或少应该都见过,就是每天或每过一段时间出水点(或其他什么东东)就会向上下左右四个方向(也有八个方向)蔓延,然后问一段时间后符合条件的坐标点有多少。只要从出水点的位置分别向四个方向深度搜索,如果符合条件(①未出界;②未被水覆盖;③海拔比出水点低),则将这个位置标记为被水覆盖的点,然后继续以这个点为中心向上下左右递归搜索。然后,最后统计一下有哪些位置被水覆盖即可。

参考代码

n, m, x, y = map(int, input().strip().split())
vector = []
for _ in range(n):
    vector.append([int(i) for i in input().strip().split()])

def dfs(x,y,center):
    for a,b in co:
        x0, y0 = x, y
        x0 += a
        y0 += b
        if 0 <= x0 < n and 0 <= y0 < m and water[x0][y0] = =0 and vector[x0][y0] < center:
            water[x0][y0] = 1
            dfs(x0,y0,center)
water = [[0]*m for i in range(n)]
water[x-1][y-1] = 1
co = ((0,-1),(0,1),(1,0),(-1,0))
center = vector[x-1][y-1]
dfs(x-1,y-1,center)
result = sum(sum(i) for i in water)
print(result)

但是,这只是针对之前每日一练中出现的、有明确指出出水点的位置的情况。而出现在本周赛的题目里是没有给出出水点位置的,那出题组是什么意图呢?由于题目说得并不清楚,对示例也并未加以说明,所以问哥猜测有以下三点:

  1. 出水点默认在中心位置(可能较小)
  2. 默认四周水会流走,不会形成积水(可能性大)
  3. 结果里被水覆盖的是积水体积,而不是格子个数(可能性大)

根据第一个猜测,如果出水点位置在中心的话,两个示例就都能满足了(示例一中出水点是第二行第三列,海拔3,示例二中出水点是第二行第二列,海拔1)。

通过上面的代码计算,可以得出被水覆盖的位置如上图中橘色点的位置。但是,这只是猜测,据此修改的代码也只能通过一半的测试数据(运气),所以这个猜测并不十分靠谱,而且如果出题人真的是因为这个理由而把出水点的位置去除的话,估计会被骂到死。

而根据第二个猜测,既然题目问的是积水,那我们就想想,是不是被水“流过”的格子不算“积水”(文字游戏)?这样一来,默认外面一圈存在最低点,所以不管出水点在哪儿,只要和外面接通了,水就都流走了,而剩下的就都是能够形成积水的洼地?如下图所示,不管出水点在哪,最终都只有图中的橘点形成积水。

但是,如果这样理解的话,示例一的答案就不正确了。

所以,问哥不得不在此再加上第三个猜测,那就是要求的并不是格子的个数,而是覆盖积水的总体积。比如上图中,第一个积水点(橘点)的高度是1,和旁边最低的高度3(可以形成积水的高度)的落差是2,右边两个积水点的高度是2和1,和3的落差分别是1和2,这样一来,2+1+2=5,答案就对上了。而同样的猜测也适用于右边示例二,积水点的高度是1,和右侧可以形成积水的最低高度2的落差为1,所以答案是1。

那,这是不是正确的猜测呢?鬼知道!

等问哥胡乱猜测到这里的时候,时间已经所剩不多,再也没心情调试了,索性点了交卷。这已经和写代码无关了——连需求是啥都没搞清楚,乱试是没有意义的。等着看满分大佬的分享,再回来更新吧。

问哥忍不住吐槽——虽然官方未必会看到,看到了也未必会改——既然改了题目,就要好好说明题意,避免错误解读,还要多找几个人评测一下,看看会不会产生误解。大家来练的是解题思路和代码的熟练度,而不是来考中文的阅读理解。还需要去猜测出题人的意图,这样的比赛让人觉得毫无意义。

1月21日更新

在洛谷上发现了类似的题,说是类似,用例都一样,得以证实当时问哥的猜测是正确的——是要测积水的体积,而且周围四个边的水是积不住的。于是再次研究了这道题。

首先,很显然,如果 n 或 m 小于等于2,也就是说地图的长或宽只有2,则整个地图都是无法积水的。

然后,我们以四周的 2*(n+m)-2 个坐标点为起点,向地图中间进行深度搜索,搜索条件是高度比当前高度相等或更高,也就是“逆流而上”,找到地图内无法积水的点位。

接下来,再从除了这些点位之外的点位开始进行第二次深度搜索,搜索条件是找到同一个“坑”的周围最矮的边。

最后,用这条最矮的边减去每个水坑的高度,得到积水的体积。对于多个水坑,累加在一起即可。

实现代码如下:

参考代码

n, m = map(int, input().strip().split())
heights = [list(map(int, input().strip().split())) for _ in range(n)]
if n <= 2 or m <= 2: # 长度或宽度为2的地图无法积水
    print(0)
else:
    # 地图四周的所有点均无法积水,作为第一次深搜的起点
    q = set([(i, 0) for i in range(n)]+[(0, j) for j in range(m)])
    q |= set([(n-i-1,m-1) for i in range(n)]+[(n-1,m-j-1) for j in range(m)])
    checked = set(q)
    while q:
        i, j = q.pop()
        for x, y in ((i+1,j),(i-1,j),(i,j+1),(i,j-1)):
            if 0<=x<n and 0<=y<m and (x,y) not in checked and heights[x][y]>=heights[i][j]: # 如果内陆的某个点比当前流水的点高度相等或更高,则说明也无法积水,继续深搜
                checked.add((x, y))
                q.add((x, y))
    water = set() # 所有剩下的点位都是会积水的
    res = 0
    for i in range(1, n-1):
        for j in range(1, m-1):
            if (i, j) not in checked and (i, j) not in water:
                toCheck = [(i, j)]
                subWater = set(toCheck) # 每一个水坑
                h = float("inf")
                while toCheck:
                    r, c = toCheck.pop()
                    for x, y in ((r+1,c),(r-1,c),(r,c+1),(r,c-1)):
                        if (x, y) in checked:
                            h = min(h, heights[x][y]) # 找到水坑周围最矮的边的高度
                        elif (x, y) not in subWater:
                            subWater.add((x, y))
                            toCheck.append((x, y))
                for x, y in subWater:
                    res += h - heights[x][y] # 累加积水的体积
                water.update(subWater) # 更新当前水坑
    print(res)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

请叫我问哥

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

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

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

打赏作者

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

抵扣说明:

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

余额充值