状压DP(状态压缩动态规划)是用二进制整数来表示集合状态的DP技巧。把"哪些元素被选中"压缩成一个整数的二进制位——第 i 位为 1 表示第 i 个元素在集合中。
核心思想:n 个元素的所有子集最多 2ⁿ 个,用 0 ~ 2ⁿ−1 的整数就能枚举。适合 n ≤ 20 的场景。
mask & (1 << i) — 非零则 i 在集合中mask | (1 << i)mask & ~(1 << i)
经典问题:TSP(旅行商问题)—— n 个城市,每个城市恰好访问一次,求最短回路。状态 dp[S][i] 表示已访问城市集合为 S、当前在城市 i 的最短路径。
下面用状压DP解 TSP 问题(n ≤ 15):
dp[S][i] 表示走过集合 S 中的城市、当前在城市 i 的最短距离。
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=15,INF=0x3f3f3f3f;
int dist[N][N],dp[1<<N][N],n;
int main(){
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++) cin>>dist[i][j];
memset(dp,0x3f,sizeof(dp));
dp[1][0]=0; // 起点在城市0
for(int S=0;S<(1<<n);S++)
for(int i=0;i<n;i++){
if(dp[S][i]==INF) continue;
for(int j=0;j<n;j++){
if(S&(1<<j)) continue;
int ns=S|(1<<j);
dp[ns][j]=min(dp[ns][j],dp[S][i]+dist[i][j]);
}
}
int full=(1<<n)-1,ans=INF;
for(int i=0;i<n;i++)
ans=min(ans,dp[full][i]); // 不用回到起点
cout<<ans;
}
& 不是 &&:位运算用 &,逻辑与用 &&,混用必错。1<<N 而不是 N:状态数是 2ⁿ,不是 n。S & (1<<j) == 0 实际是 S & ((1<<j)==0),必须加括号:(S & (1<<j)) == 0。continue:跳过已访问城市或不可达状态,否则会 TLE。dp[1][0] = 0,注意 1 的含义是二进制只有第 0 位为 1(城市 0)。