2024蓝桥杯每日一题(状压DP)

备战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程序代码】

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

【输入样例】

python 复制代码
1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0

【输出样例】

python 复制代码
1
0
1
2
3
5
144
51205

【解题思路】

定义状态fij,表示前i-1列已经排好,且第i列的状态为j的方案数,所以转移方程应该为f[i][j] += f[i-1][k],k的状态应该为首先相邻的零的数量是偶数,同时其与j的或也是一个合法的状态即相邻的零的数量是偶数,同时二者的与应该为0。为了加快效率,可以预处理出所有的合法的列的状压,也预处理出每一个合法的列能够邻接的合法状态。

【Python程序代码】

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程序代码】

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) 的余数。

【数据范围】

【输入样例】

python 复制代码
4 4 3

【输出样例】

python 复制代码
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程序代码】

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)
相关推荐
HPC_fac1305206781634 分钟前
以科学计算为切入点:剖析英伟达服务器过热难题
服务器·人工智能·深度学习·机器学习·计算机视觉·数据挖掘·gpu算力
网易独家音乐人Mike Zhou3 小时前
【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter)
c语言·python·单片机·物联网·算法·嵌入式·iot
安静读书3 小时前
Python解析视频FPS(帧率)、分辨率信息
python·opencv·音视频
小陈phd3 小时前
OpenCV从入门到精通实战(九)——基于dlib的疲劳监测 ear计算
人工智能·opencv·计算机视觉
Guofu_Liao4 小时前
大语言模型---LoRA简介;LoRA的优势;LoRA训练步骤;总结
人工智能·语言模型·自然语言处理·矩阵·llama
小二·5 小时前
java基础面试题笔记(基础篇)
java·笔记·python
小喵要摸鱼6 小时前
Python 神经网络项目常用语法
python
Swift社区7 小时前
LeetCode - #139 单词拆分
算法·leetcode·职场和发展
Kent_J_Truman7 小时前
greater<>() 、less<>()及运算符 < 重载在排序和堆中的使用
算法
一念之坤8 小时前
零基础学Python之数据结构 -- 01篇
数据结构·python