状态压缩:将集合的状态用一个数字表示,这个数字的每一位是0或者1,表示着一个元素的两种状态。
此处应该介绍一下位运算和性质(脑补一下?)
几大类状压dp:
- TSP问题
- 密铺问题(轮廓线法)
- 插头dp
- 传统的状压dp问题
后面的两类问题并不会。
题目
TSP问题
题意
- 从0开始,经过图中所有的点有且仅有一次,最终回到原点
- 这个图是完全图
- 问最小的路径距离
题解
- 状态设计: dp[i][state]:到达i这个点,状态为state的最短路,state的二进制每一位为1表示访问过了,0表示没有被访问过
- 状态转移:
if(state|(1<<j) == 0) dp[j][state|(1<<j)] = min(dp[j][state|(1<<j)], dp[i][state]+dis[i][j]);
- 边界:dp[0][0] = 0;
- 结果: dp[0][(1<<n)-1]
AC代码
注意min()被重写了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| #include <iostream> #include<cstring> using namespace std; int dis[16][16]; int n; int dp[17][1<<16]; int min(int x, int y){ if(x == -1) return y; if(y == -1) return x; if(x<=y) return x; else return y; } void solve(){ memset(dp, -1, sizeof(dp)); dp[0][0] = 0; for(int state=0; state<(1<<n); state++){ for(int i=0; i<n; i++){ if(dp[i][state] != -1){ for(int j=0; j<n; j++){ if(dp[j][state|(1<<j) == 0]){ dp[j][state|(1<<j)] = min(dp[i][state]+dis[i][j], dp[j][state|(1<<j)]); } } } } } cout<<dp[0][(1<<n)-1]<<endl; } int main() { while(cin>>n&&n){ for(int i=0; i<=n; i++){ for(int j=0; j<=n; j++){ cin>>dis[i][j]; } } n++; for(int k=0; k<n; k++){ for(int i=0; i<n; i++){ for(int j=0; j<n; j++){ dis[i][j] = min(dis[i][j], dis[i][k]+dis[k][j]); } } } solve(); } return 0; }
|
问题
Mondriaan’s Dream
题意
- 在n*m的格子中铺满1*2或者2*1的格子
- 一共有多少种铺设方案
题解
AC代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #include <cstdio> #include <cstring> using namespace std; int h, w; const int maxn = 11; long long dp[maxn+3][1<<maxn]; void dfs(int column, int row, int state, int nex){ if(column == w){ dp[row+1][nex] += dp[row][state]; return ; } if(((1<<column)&state) > 0)dfs(column+1, row, state, nex); else{ dfs(column+1, row, state, (nex|(1<<column))); if(column+1<w&&(state&(1<<(column+1))) == 0) dfs(column+2, row, state, nex); } } int main() { while(scanf("%d%d", &h, &w)&&h+w!=0){ memset(dp, 0, sizeof(dp)); dp[0][0] = 1; for(int i=0; i<h; i++){ for(int state = 0; state<(1<<w); state++){ if(dp[i][state]){ dfs(0, i, state, 0); } } } printf("%lld\n", dp[h][0]); } return 0; }
|
未解决的问题
插头dp,轮廓线题目推荐
看艾教的ppt,很多都不会emmmm被菜醒。