A卡片
cards = [2021]*10
i = 1
while True:
s = str(i)
for j in s:
cards[int(j)] -= 1
flag = False
for j in cards:
if j < 0:
flag = True
break
if flag:
break
i += 1
print(i-1)
# 3181
B直线
cnt = 0
dots = [(i, j) for i in range(20) for j in range(21)] # 生成所有点
lines = set()
for i in range(len(dots)):
for j in range(i+1, len(dots)):
if dots[j][0]-dots[i][0] != 0:
x1, x2 = dots[i][0], dots[j][0]
y1, y2 = dots[i][1], dots[j][1]
k = (y2-y1)/(x2-x1)
b = (y1*(x2-x1)-x1*(y2-y1))/(x2-x1) # 这么写不会炸精度
lines.add((k, b))
print(len(lines)+20)
# 40257
C货物摆放
from math import *
n = 2021041820210418
cnt = 0
fac = []
for i in range(1, int(sqrt(n))+1):
if n % i == 0:
fac.append(i)
fac.append(n//i)
for i in fac:
for j in fac:
for k in fac:
if i*j*k == n:
cnt += 1
print(cnt)
# 2430
D路径
from math import *
n = 2021
vis = [False]*(n+1)
dis = [float('inf')]*(n+1)
graph = [[float('inf') for i in range(n+1)] for j in range(n+1)]
for i in range(1, n+1):
for j in range(1, n+1):
if abs(i-j) > 21:
continue
graph[i][j] = lcm(i, j)
dis[1] = 0
while True:
point = 0
for i in range(1, n+1):
if (not vis[i]) and dis[i] < dis[point]:
point = i
if point == 0:
break
vis[point] = True
for i in range(1, n+1):
if dis[i] > dis[point]+graph[point][i]:
dis[i] = dis[point]+graph[point][i]
print(dis[n])
# 10266837
E回路计数
答案:881012367360
思路:状压dp,不会dp位操作的建议先看一下位操作
先用一个邻接矩阵e来表示某两个教学楼之间能否通行,e[i][j]=True表示i号和j号可以通行,否则不能通行。利用gcd函数求出两个数的最大公因数(原理为广义欧几里得除法),若为1代表互质,即可以通行。
设置一个二维数组f,f[i][j]表示在状态i下从1号到j号的方案数。这里解释一下状态的含义:一共21个教学楼,对应21个二进制位,某一位为1表示访问对应的教学楼,否则为不访问,易知状态i最大取值为 2 21 − 1 2^{21}-1 221−1。
由于1号和其他所有号都互质,所以1号和其他所有都联通。题目要求从1号出发走遍所有教学楼最终再回到1号,也就是状态i的所有二进制位上都是1,十进制就是 2 21 − 1 2^{21}-1 221−1,不妨记为a。回到1号我可以从2号返回,也可从3号,4号,……,21号返回。那么最终方案总数就是这20种返回的累加和:
∑ j = 2 21 f [ a ] [ j ] \sum_{j=2}^{21}f[a][j] j=2∑21f[a][j]
对于某一个特定的状态i,21个二进制位上有0有1。对于其中每一位的1,代表访问对应的教学楼,那么是从哪个教学楼来到这个教学楼的呢?自然是从其他为1的位对应的教学楼,前提是这两个教学楼要联通,这就要用到前面的邻接矩阵e了,后面表述为了方便省略了联通的前提。所以状态转移时,f[i][j](j代表了i的某一位上的1所对应的教学楼的编号)要加上其他所有为1的位的f[i][j](这个j和前面的j不是同一个,它代表了其他为1的二进制位所对应的教学楼编号)。对于某一状态i,其中每一个二进制位1都有可能是从其他为1的为过来的,所以要遍历状态i二进制的所有1,对每一位1都进行上述状态转移操作。
代码如下,跑起来略慢:
def gcd(x, y):
if x % y == 0:
return y
return gcd(y, x % y)
n = 21
m = 1 << n
e = [[False]*n for _ in range(n)] # 1-21的编号映射成0-20
f = [[0]*n for _ in range(m)] # f[i][j]表示在状态i下从1号到j号的方案数
# 状态指二进制下对应位的教学楼访问与否,转成十进制即为状态i
for i in range(n):
for j in range(n):
if gcd(i+1, j+1) == 1:
e[i][j] = True
f[1][0] = 1 # 初始化,在状态1下从第0个教学楼到第0个教学楼的方案数为1
for i in range(1, m):
for j in range(n):
if (i >> j) & 1 == 1: # 确保该状态是访问j号的
for k in range(n):
if ((i-(1 << j)) >> k) & 1 == 1 and e[k][j]:
f[i][j] += f[i-(1 << j)][k]
ans = 0
for i in range(1, n):
ans += f[m-1][i]
print(ans)
# 881012367360
F时间显示
time = int(input())
time %= 24*60*60*1000
h = time//(60*60*1000)
time -= h*60*60*1000
m = time//(60*1000)
time -= m*60*1000
s = time//1000
print("{:0>2d}:{:0>2d}:{:0>2d}".format(h, m, s))
G杨辉三角形
def generate(tri):
tmp = [1]
for i in range(len(tri)-1):
tmp.append(tri[i]+tri[i+1])
tmp.append(1)
return tmp
n = int(input())
res = 0
tri = [1]
while True:
if n in tri:
res += tri.index(n)+1
break
res += len(tri)
tri = generate(tri)
print(res)
# 运行超时,40分
H左孩子右兄弟
可能有以下 3 种 (这里只列出 3 种,并不是全部) 不同的 “左孩子右兄弟” 表示:
n = int(input())
tree = [[] for i in range(n+1)]
for i in range(2, n+1):
tree[int(input())].append(i)
def dfs(i):
if len(tree[i]) == 0:
return 0
maxn = 0
for j in tree[i]:
maxn = max(maxn, dfs(j))
return len(tree[i])+maxn
print(dfs(1))
# 90分