备战2024年蓝桥杯 -- 每日一题
Python大学A组
试题一:毕业旅行问题
试题二:蒙德里安的梦想
试题三:最短Hamilton路径
试题四:国际象棋
试题一:毕业旅行问题
【题目描述】
小明目前在做一份毕业旅行的规划。打算从北京出发,分别去若干个城市,然后再回到北京,每个城市之间均乘坐高铁,且每个城市只去一次。由于经费有限,小明希望能够通过合理的路线安排尽可能的省些路上的花销。给定一组城市和每对城市之间的火车票的价钱,找到每个城市只访问一次并返回起点的最小车费花销。
注意:北京为 1 号城市。
【输入格式】
第一行包含一个正整数 n,表示城市个数。
接下来输入一个 n 行 n 列的矩阵,表示城市间的车票价钱。
【输出格式】
输出一个整数,表示最小车费花销。
【数据范围】
1<n≤20,包括北京
车票价格均不超过 1000元。
【输入样例】
4
0 2 6 5
2 0 4 4
6 4 0 2
5 4 2 0
【输出样例】
13
【解题思路】
比较标准的状态压缩问题,可以看成固定起点的一个最短哈密顿路径问题,不过要加上最后一个点到北京的距离,所以在初始化时可以令f[1<<i][i] = m[0][i],然后求最短哈密顿距离,不过得提前判断一下状态st,如果st第0位是1同时st!=(1<<n)-1那么当前状态无效,跳过。最后的答案是f[(1<<n)-1][0]。转移方程为:f[st][i] = min(f[st][i] , f[st - (1<<i)][j] + m[j][i])
【Python程序代码】
n = int(input())
m = []
for i in range(n):
m.append(list(map(int,input().split())))
inf,fin = 1e9,1<<n
f = [[int(1e9)]*(n+5) for _ in range((1<<n)+10)]
for i in range(1,n):f[1<<i][i]=m[0][i]
for st in range(fin):
if st&1 and st!=fin-1:continue
for i in range(n):
if (1<<i) & st:
for j in range(n):
if (st-(1<<i)) & (1<<j):
f[st][i] = min(f[st][i],f[st-(1<<i)][j] + m[j][i])
print(f[fin-1][0])
试题二: 蒙德里安的梦想
【题目描述】
求把 N×M 的棋盘分割成若干个 1×2的长方形,有多少种方案。例如当 N=2,M=4时,共有 5种方案。当 N=2,M=3时,共有 3 种方案。
如下图所示:
【输入格式】
输入包含多组测试用例。
每组测试用例占一行,包含两个整数 N 和 M。
当输入用例 N=0,M=0 时,表示输入终止,且该用例无需处理。
【输出格式】
每个测试用例输出一个结果,每个结果占一行。
【数据范围】
1≤N,M≤11
【输入样例】
1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0
【输出样例】
1
0
1
2
3
5
144
51205
【解题思路】
定义状态fij,表示前i-1列已经排好,且第i列的状态为j的方案数,所以转移方程应该为f[i][j] += f[i-1][k],k的状态应该为首先相邻的零的数量是偶数,同时其与j的或也是一个合法的状态即相邻的零的数量是偶数,同时二者的与应该为0。为了加快效率,可以预处理出所有的合法的列的状压,也预处理出每一个合法的列能够邻接的合法状态。
【Python程序代码】
n,m = map(int,input().split())
while n and m:
fin = 1<<n
f = [[0]*(fin+10) for _ in range(m+10)] # fij表示前i-1列已经铺好,第i列状态为j的方案数
st = [0]*(fin+10)
state = [[] for _ in range(fin+10)]
for i in range(fin):
cnt = 0
flag = 0
for j in range(n):
if i & (1<<j):
if cnt%2:
flag=1
break
else:
cnt +=1
if cnt%2:flag=1
if not flag:st[i]=1
for i in range(fin):
for j in range(fin):
if i&j==0 and st[i|j]:
state[i].append(j)
f[0][0]=1
for i in range(1,m+1):
for j in range(fin):
for k in state[j]:
f[i][j] += f[i-1][k]
print(f[m][0])
n,m = map(int,input().split())
试题三:最短Hamilton路径
【题目描述】
给定一张 n 个点的带权无向图,点从 0∼n−1 标号,求起点 0 到终点 n−1 的最短 Hamilton 路径。Hamilton 路径的定义是从 0 到 n−1 不重不漏地经过每个点恰好一次。
【输入格式】
第一行输入整数 n。
接下来 n 行每行 n 个整数,其中第 i 行第 j 个整数表示点 i到 j 的距离(记为 a[i,j])。
对于任意的 x,y,z,数据保证 a[x,x]=0,a[x,y]=a[y,x]=0 且 a[x,y]+a[y,z]≥a[x,z]。
【输出格式】
·输出一个整数,表示最短 Hamilton 路径的长度。
【数据范围】
1≤n≤20
0≤a[i,j]≤107
【解题思路】
第一题的缩减版,初态f[1][0]=0,也就是在初始点的值为0,其他的参考第一题。
【Python程序代码】
n = int(input())
a = []
for i in range(n):
a.append(list(map(int,input().split())))
inf = int(1e9)
f = [[inf]*(n+5) for _ in range((1<<n)+10)]
f[1][0]=0
fin = 1<<n
for st in range(fin):
for i in range(n):
if st&(1<<i):
for j in range(n):
if (st-(1<<i)) & 1<<j:
f[st][i] = min(f[st][i],f[st-(1<<i)][j] + a[j][i])
print(f[fin-1][n-1])
第四题:国际象棋
众所周知,“八皇后” 问题是求解在国际象棋棋盘上摆放 8 个皇后,使得两两之间互不攻击的方案数。已经学习了很多算法的小蓝觉得 “八皇后” 问题太简单了,意犹未尽。作为一个国际象棋迷,他想研究在 N×M的棋盘上,摆放 K 个马,使得两两之间互不攻击有多少种摆放方案。由于方案数可能很大,只需计算答案除以 1000000007 (即 109+7) 的余数。
如下图所示,国际象棋中的马摆放在棋盘的方格内,走 “日” 字,位于 (x,y) 格的马(第 x 行第 y 列)可以攻击 (x+1,y+2)、(x+1,y−2)、(x−1,y+2)、(x−1,y−2)、(x+2,y+1)、(x+2,y−1)、(x−2,y+1)和 (x−2,y−1) 共 8 个格子。
【输入格式】
输入一行包含三个正整数 N,M,K分别表示棋盘的行数、列数和马的个数。
【输出格式】
输出一个整数,表示摆放的方案数除以 1000000007 (即 109+7) 的余数。
【数据范围】
【输入样例】
4 4 3
【输出样例】
276
【解题思路】
定义状态f[i][a][b][j]表示前i列已近防止好了j个马,第i列状态为b,第i-1列状态为a,所以状态转移方程应该为:f[i][a][b][j] = (f[i][a][b][j] + f[i-1][c][a][k]),首先相邻的状态a,b之间需要判断是否合法即判断:a&(b<<2) or b&(a<<2),后面再枚举c的状态,也需要判断c与a和c与b之间是否合法。然后枚举马的数量,应该为k应该是小于b状态的1的数量的数,同时需要保证总的马的数量不超过1.
【Python程序代码】
n,m,k = map(int,input().split())
fin,p = 1<<n,10**9+7
f = [[[[0]*(22) for _ in range(fin+5)] for i in range(fin+5)] for j in range(110)]
# f i a b j += f i-1 c a j-cnt1(b)
def get1(x):
cnt = 0
while x:
cnt += x%2
x//=2
return cnt
f[0][0][0][0]=1
for i in range(1,m+1):
for a in range(fin):
for b in range(fin):
if a&(b<<2) or b&(a<<2):continue
for c in range(fin):
if a&(c<<2) or c&(a<<2):continue
if c&(b<<1) or b&(c<<1):continue
t = get1(b)
for j in range(t,k+1):
f[i][a][b][j] = (f[i][a][b][j] + f[i-1][c][a][j-t])%p
res = 0
for a in range(fin):
for b in range(fin):
res = (res + f[m][a][b][k])%p
print(res)