牛客周赛 Round 153

在工位摸鱼,遂把周赛打了,尝试用AI辅助写一写python。


A 小红的字符串处理

思路

签到,因为字符串相当于一个序列,所以可以用 ".".join(s) 来实现在序列中插入字符 .,这样码量少一点。

代码

python 复制代码
s = input()
s = ".".join(s)
print(s)

B 小红的菊花构造

思路

签到,让 1 ∼ n 1\sim n 1∼n 中除了 k 以外的点往 k 上连一条边就行了。这题主要是为了介绍菊花树这个概念。

代码

python 复制代码
def main():
	import sys
	data = sys.stdin.read().strip().split()
	if not data:
		return
	try:
		n = int(data[0])
		k = int(data[1]) if len(data) > 1 else 0
	except:
		return
	out_lines = []
	for i in range(1, n+1):
		if i == k:
			continue
		out_lines.append(f"{i} {k}")
	sys.stdout.write("\n".join(out_lines))

if __name__ == '__main__':
	main()

C D 小红的swap

思路

CD题目是一样的,只是 D 题需要操作次数最少,所以这题写一个 D 的代码就行了。

题面可能不是很好理解,字符 c 就是一个缓存区,一开始存储的字符 2,每次和字符串 s 交换一个字符。比如 s 是 0011,c 和 s 的第二个字符交换,就得到了 c:0 和 s:0211。最终目的是通过若干次交换,使得字符串 s 和 t 一样。

这个题保证了 s 和 t 有相同数量的 0 0 0 和 1 1 1,而且 p 一定大于等于操作次数最少的操作次数,因此不存在无解的情况,所以构造的时候不需要特判。

操作思路如下:首先因为 s 和 t 有相同数量的 0 0 0 和 1 1 1,因此 s 和 t 不同时, s i s_i si 为 0 0 0 但 t i t_i ti 为 1 1 1 的位置的个数一定等于 s i s_i si 为 1 1 1 但 t i t_i ti 为 0 0 0 的位置的个数。当我们交换 s 是 0 的位置时,c 就得到了 0,我们想要把它放到 s i s_i si 为 1 1 1 但 t i t_i ti 为 0 0 0 的位置上,位置 i i i 就相同了,同时 c 就得到了 1,同理再把这个 1 放到 s i s_i si 为 0 0 0 但 t i t_i ti 为 1 1 1 的位置上,以此类推。

所以我们就是要把 s i s_i si 为 0 0 0 但 t i t_i ti 为 1 1 1 的位置以及 s i s_i si 为 1 1 1 但 t i t_i ti 为 0 0 0 的位置统计出来,然后交换前者的某个位置,交换后者的某个位置的操作交替进行,最后再把一开始交换的位置(字符串 s 那里放着字符 c 一开始存放的字符 2)交换回来就可以了。

代码

python 复制代码
import sys

def main():
	data = sys.stdin.read().strip().split()
	if not data:
		return
	# 读取整数 n(可忽略)和两个字符串 s, t
	# 假设输入为: n s t
	n = int(data[0])
	if n == 0:
		print(0)
		return
	
	if len(data) < 3:
		return
	# n = int(data[0])
	s = data[1].strip()
	t = data[2].strip()

	a = []  # 在 s 中为 '0' 但在 t 中为 '1' 的下标(1-based)
	b = []  # 在 t 中为 '0' 但在 s 中为 '1' 的下标(1-based)

	L = min(len(s), len(t)) # 无意义,因为题目保证长度相同
	for i in range(L):
		if s[i] == '0' and t[i] == '1':
			a.append(i+1) # 下标从 1 开始
		elif t[i] == '0' and s[i] == '1':
			b.append(i+1)

	# 输出结果:2*len(a)+1,然后依次输出a、b,最后输出a的第一个内容
	if a and len(a) == len(b):
		print(2 * len(a) + 1)
		for i in range(len(a)):
			print(a[i])
			print(b[i])
		print(a[0])
	else:
		print(0)

if __name__ == '__main__':
	main()

小红的分割线

思路

首先介绍一点点线性代数的知识,向量叉积的运算结果有正负之分。对于 a ⃗ × b ⃗ \vec a\times \vec b a ×b ,若结果为正,则表示向量 b ⃗ \vec b b 在向量 a ⃗ \vec a a 的逆时针方向(角度为 0 ∼ π 0\sim \pi 0∼π);反之,若结果为负,则表示向量 b ⃗ \vec b b 在向量 a ⃗ \vec a a 的顺时针方向。

怎么用这个性质:假设现在有三个点 v 1 v_1 v1, v 2 v_2 v2, v 3 v_3 v3,不妨设 a ⃗ = v 1 v 2 → , b ⃗ = v 1 v 3 → \vec a=\overrightarrow{v_1v_2}, \vec b=\overrightarrow{v_1v_3} a =v1v2 ,b =v1v3 。这时 a ⃗ \vec a a 就代表了 v 1 v_1 v1, v 2 v_2 v2 组成的直线的方向向量,若 b ⃗ \vec b b 在 a ⃗ \vec a a 的逆时针方向,就说明 v 3 v_3 v3 在直线左边,同理若 b ⃗ \vec b b 在 a ⃗ \vec a a 的顺时针方向,就说明 v 3 v_3 v3 在直线右边(若等于 0 ,说明三点共线)。

接下来就好办了,枚举 v 1 v_1 v1, v 2 v_2 v2(即枚举直线上两点),然后枚举 v 3 v_3 v3,通过向量叉积判断直线两侧点的个数,统计答案即可。

代码

python 复制代码
import sys

def main():
	data = sys.stdin.read().strip().split()
	if not data:
		return
	it = iter(data)
	n = int(next(it))
	pts = []
	for _ in range(n):
		x = int(next(it))
		y = int(next(it))
		pts.append((x, y))

	ans = 0
	for i in range(n):
		x1, y1 = pts[i]
		for j in range(i+1, n):
			x2, y2 = pts[j]
			left = 0
			right = 0
			for k in range(n):
				if k == i or k == j:
					continue
				xk, yk = pts[k]
				# cross product (p2-p1) x (pk-p1)
				cross = (x2 - x1) * (yk - y1) - (y2 - y1) * (xk - x1)
				if cross > 0:
					left += 1
				elif cross < 0:
					right += 1
				# cross == 0 -> on the line, ignored
			if left == right:
				ans += 1

	print(ans)

if __name__ == '__main__':
	main()

小红的网格图构造2.0

思路

朴素想法是给所有 ( 2 ∗ i , 2 ∗ j ) (2*i,2*j) (2∗i,2∗j) 其中 i ≥ 0 , j ≥ 0 , 2 ∗ i < n , 2 ∗ j < m i\ge 0,j\ge0,2*i\lt n,2*j\lt m i≥0,j≥0,2∗i<n,2∗j<m,填上 1 1 1,其余位置补 0 0 0,就能满足题目任意 2 × 2 2\times 2 2×2 的区域包含 1 1 1 的约束。

这种构造方式显然是成立的,因为在任意行上,1 之间都只相差一格,间隔一定放不下长为 2 的窗口,因此长为 2 的窗口一定会包含一个 1,同理列。

这种构造方式也是最少的,因为如果移除任何一处1,或是拉长两个 1 之间的距离,就会导致有某个位置可以放下长为 2 的窗口,这意味着这个窗口内不包含 1。

这种构造方式的核心是相邻 1 间隔为 1。因此填充 ( 2 ∗ i , 2 ∗ j ) (2*i,2*j) (2∗i,2∗j), ( 2 ∗ i + 1 , 2 ∗ j ) (2*i+1,2*j) (2∗i+1,2∗j), ( 2 ∗ i , 2 ∗ j + 1 ) (2*i,2*j+1) (2∗i,2∗j+1), ( 2 ∗ i + 1 , 2 ∗ j + 1 ) (2*i+1,2*j+1) (2∗i+1,2∗j+1) 都是可以的。

问题在于 1 应该先填充在 ( 2 ∗ i , 2 ∗ j ) (2*i,2*j) (2∗i,2∗j), ( 2 ∗ i + 1 , 2 ∗ j ) (2*i+1,2*j) (2∗i+1,2∗j), ( 2 ∗ i , 2 ∗ j + 1 ) (2*i,2*j+1) (2∗i,2∗j+1), ( 2 ∗ i + 1 , 2 ∗ j + 1 ) (2*i+1,2*j+1) (2∗i+1,2∗j+1) 的哪一种位置上。

不难想到如果 1 1 1 的个数 k k k 如果很小,就填充在 ( 2 ∗ i + 1 , 2 ∗ j + 1 ) (2*i+1,2*j+1) (2∗i+1,2∗j+1) 上,如果 k k k 小到连这些位置都填不下,就说明无解。如果够多,就可以填充在 ( 2 ∗ i , 2 ∗ j ) (2*i,2*j) (2∗i,2∗j)。

靠外,在边界上就容易被 "卡" 没,总数就少。反之靠内,就不容易被 "卡" 没,总数就多。

如果 k k k 继续增大,比 ( 2 ∗ i , 2 ∗ j ) (2*i,2*j) (2∗i,2∗j) 位置个数要多,那么显然可以填到 ( 2 ∗ i + 1 , 2 ∗ j ) (2*i+1,2*j) (2∗i+1,2∗j), ( 2 ∗ i , 2 ∗ j + 1 ) (2*i,2*j+1) (2∗i,2∗j+1)。但是当填到 ( 2 ∗ i + 1 , 2 ∗ j + 1 ) (2*i+1,2*j+1) (2∗i+1,2∗j+1) 的位置上时, 0 0 0 就无法满足约束了,因此此时就无解了。

其实到这里不难发现, 0 0 0 和 1 1 1 角色是类似的,我们不关心是在 1 1 1 的网格上填充 0 0 0,还是在 0 0 0 的网格上填充 1 1 1。因此我们可以在 k ≥ n × m 2 k\ge \dfrac{n\times m}2 k≥2n×m 的时候在 0 0 0 的网格上填 1 1 1,反之在 k < n × m 2 k\lt \dfrac{n\times m}2 k<2n×m 的时候在 1 1 1 的网格上填 0 0 0。这样就不需要讨论当 k k k 比较小时的特判和填法,同时不管在哪个网格上填谁,我们可以复用同一种写法,具体实现见代码

代码

我们如果 k < n × m 2 k\lt \dfrac{n\times m}2 k<2n×m 就让 01 身份反转一下,解法就都是在 0 0 0 的网格上填充 1 1 1,用 flag 记录是否翻转,最后输出时根据 flag 判断 01 身份是否反转。

python 复制代码
n, m, k = map(int, input().split())
if not (k >= (n//2)*(m//2) and n*m - k >= (n//2)*(m//2)):
    print("No")
    exit()
else:
    print("Yes")

grid = [[0] * m for _ in range(n)]
flag = False
if k < n*m/2:
    flag = True
    k = n*m - k

cnt = 0
for i in range((n + 1) // 2):
    for j in range((m + 1) // 2):
        if cnt >= k:
            break
        grid[2 * i][2 * j] = 1
        cnt += 1
    if cnt >= k:
        break

if cnt < k:
    for i in range(n // 2):
        for j in range((m + 1) // 2):
            if cnt >= k:
                break
            grid[2 * i + 1][2 * j] = 1
            cnt += 1
        if cnt >= k:
            break

if cnt < k:
    for i in range((n + 1) // 2):
        for j in range(m // 2):
            if cnt >= k:
                break
            grid[2 * i][2 * j + 1] = 1
            cnt += 1
        if cnt >= k:
            break

if flag:
    for i in range(n):
        for j in range(m):
            grid[i][j] = 1 - grid[i][j]

for row in grid:
    print("".join(map(str, row)))

G 小红的网格图构造1.0

思路

这题看着像 F,但是约束完全变了,所以是完全不同的两道题。

朴素想法是类似的,给所有 ( 2 ∗ i , 2 ∗ j ) (2*i,2*j) (2∗i,2∗j) 其中 i ≥ 0 , j ≥ 0 , 2 ∗ i < n , 2 ∗ j < m i\ge 0,j\ge0,2*i\lt n,2*j\lt m i≥0,j≥0,2∗i<n,2∗j<m,填上 1 1 1,其余位置补 0 0 0,就能满足题目任意 2 × 2 2\times 2 2×2 的区域包含 1 1 1 的这条约束。

问题在于如何构造连通块数量 = k =k =k。

经过上面的构造,此时连通块数量等于 1 的数量,即 ⌊ n 2 ⌋ × ⌊ m 2 ⌋ \left\lfloor\dfrac n2\right\rfloor\times \left\lfloor\dfrac m2\right\rfloor ⌊2n⌋×⌊2m⌋ 个连通块,我们通过把相邻 1 之间的 0 填充成 1(即填充 ( 2 ∗ i + 1 , 2 ∗ j ) (2*i+1,2*j) (2∗i+1,2∗j), ( 2 ∗ i , 2 ∗ j + 1 ) (2*i,2*j+1) (2∗i,2∗j+1)),就可以减少一个连通块,最终把所有 1 都串联起来,最少是剩下 1 个连通块。

增加连通块也好办,就是填充 ( 2 ∗ i + 1 , 2 ∗ j + 1 ) (2*i+1,2*j+1) (2∗i+1,2∗j+1),让相邻行的 1 错开。最多能填充到 ⌈ n m 2 ⌉ \left\lceil\dfrac {nm}2\right\rceil ⌈2nm⌉ 个连通块。

所以代码就很好写了,不过需要注意 n m 可能是 1,此时不存在 2 ∗ 2 2*2 2∗2 的网格,因此依然可以满足题目任意 2 × 2 2\times 2 2×2 的区域包含 1 1 1 的这条约束。这时即使 k=0 也可以构造,所以此时需要特判。

代码

python 复制代码
import math


def main():
    try:
        n, m, k = map(int, input().split())
    except ValueError:
        return

    if k == 0 and (n == 1 or m == 1):
        print("Yes")
        for _ in range(n):
            print("0" * m)
        return
    elif k > 0 and k <= (n * m + 1) // 2:
        print("Yes")
    else:
        print("No")
        return

    grid = [[0] * m for _ in range(n)]
    
    if k <= ((n+1)//2)*((m+1)//2):
        cnt = 0
        for i in range((n+1)//2):
            for j in range((m+1)//2):
                grid[2*i][2*j] = 1
                cnt += 1
        
        for i in range((n+1)//2):
            for j in range((m+1)//2-1):
                if cnt > k:
                    grid[2*i][2*j+1] = 1
                    cnt -= 1
        
        for i in range((n+1)//2-1):
            if cnt > k:
                grid[2*i+1][2*j] = 1
                cnt -= 1
    else:
        cnt = 0
        for i in range((n+1)//2):
            for j in range((m+1)//2):
                grid[2*i][2*j] = 1
                cnt += 1
        
        for i in range(n//2):
            for j in range(m//2):
                if 2*i+1 < n and 2*j+1 < m and cnt < k:
                    grid[2*i+1][2*j+1] = 1
                    cnt += 1

    print("\n".join("".join(map(str, row)) for row in grid))


if __name__ == "__main__":
    main()
相关推荐
yaoxin5211231 小时前
470. Java 反射 - Member 接口与 AccessFlag
java·开发语言·python
groundhappy1 小时前
idalib安装和codex ida-mcp配置
linux·开发语言·python
培之2 小时前
RTX 5090 安装 pytorch3d
人工智能·pytorch·python
Metaphor6922 小时前
使用 Python 在 PowerPoint 中创建折线图和条形图
python·信息可视化·powerpoint·图表
qizayaoshuap3 小时前
# ❌ 井字棋 — 鸿蒙ArkTS Minimax AI算法与博弈系统设计
人工智能·算法·华为·harmonyos
m沐沐3 小时前
【深度学习】卷积神经网络 数据增强、保存最优模型实现,详细解读
人工智能·python·深度学习·机器学习·cnn·数据增强
天青色等烟雨..3 小时前
全流程ArcGISPro空间分析、三维建模、可视化及Python融合应用技术
开发语言·python
极光代码工作室4 小时前
基于Spark的日志监控与分析平台
大数据·hadoop·python·spark·数据可视化
AOwhisky4 小时前
Python 学习笔记(第十三期)——运维自动化(下·前篇):远程命令执行——paramiko基础篇
运维·python·学习·云原生·自动化·运维开发·paramiko