状压dp一些题目
dp-bitmasks 在中国叫做 状压dp
https://usaco.guide/gold/dp-bitmasks?lang=cpp
题目
https://cses.fi/problemset/task/1690/
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1000000007;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
// 用邻接矩阵保存图的信息,graph[i][j]为true表示存在从城市 i 到城市 j 的单向航班
vector<vector<bool>> graph(n, vector<bool>(n, false));
for (int i = 0; i < m; i++){
int a, b;
cin >> a >> b;
// 注意将城市编号从1调整到0(0-based下标)
graph[a - 1][b - 1] = true;
}
// dp[mask][i] 表示状态为 mask(一个二进制掩码,表示已经访问的城市集合),且当前所在城市为 i 的路线数
// 初始状态为只访问了起点城市 1(下标 0),即 mask = 1<<0
vector<vector<int>> dp(1 << n, vector<int>(n, 0));
dp[1 << 0][0] = 1;
// 枚举所有的状态(掩码)
for (int mask = 0; mask < (1 << n); mask++){
for (int i = 0; i < n; i++){
// 如果当前状态 mask 下,不在城市 i 的方案数为 0,则跳过
if(dp[mask][i] == 0) continue;
// 尝试从城市 i 飞往任意未访问过的城市 j
for (int j = 0; j < n; j++){
// 如果城市 j 未访问且存在从 i 到 j 的航班
if (!(mask & (1 << j)) && graph[i][j]) {
int nextMask = mask | (1 << j);
dp[nextMask][j] = (dp[nextMask][j] + dp[mask][i]) % MOD;
}
}
}
}
// 最终答案为所有城市均已访问且终点为城市 n(下标 n-1)的方案数
cout << dp[(1 << n) - 1][n - 1] << "\n";
return 0;
}
说明
图的存储
采用邻接矩阵graph
存储城市之间的航班信息。由于城市数较小(n ≤ 20),使用邻接矩阵可以方便判断两城市间是否存在航班。状态定义
定义dp[mask][i]
表示访问状态为mask
、当前位于城市 i 时的方案数。mask
是一个整数,其二进制位表示对应城市是否已经被访问。例如:如果n=4
,则状态mask = 1011
表示访问了城市 1、2、4(注意下标从0开始)。状态转移
对于每个状态mask
和当前城市i
,尝试从i
出发飞到所有未访问的城市j
(即mask
的第 j 位为 0 且graph[i][j]
为 true),更新新的状态mask | (1<<j)
的方案数。初始化和答案
初始状态为只访问起点城市 1,即dp[1<<0][0]=1
。最终要求的答案为所有城市均访问到(即mask=(1<<n)-1
)并且路径终点为城市 n(下标 n-1)的方案数。
这样实现的复杂度为 O(2^n * n^2),对于 n ≤ 20 是可行的。