bzoj2595
状压dp+斯坦纳树
原先就想过这个问题
其实就是spfa优化dp,跟3875很像
有两种转移方式,一种是枚举子集,另一种是拓展新的点,每次我们先枚举子集更新,再spfa更新dp,就是拓展新的点
#include<bits/stdc++.h> using namespace std; const int N = 11, inf = 0x3f3f3f3f, dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1}; int n, m, p; int bin[N], a[N][N], t[N][N][1 << N], dp[N][N][1 << N], vis[N][N], pre[N][N][1 << N][3]; void dfs(int x, int y, int S) { if(!S) return; vis[x][y] = 1; dfs(pre[x][y][S][0], pre[x][y][S][1], pre[x][y][S][2]); if(x == pre[x][y][S][0] && y == pre[x][y][S][1]) dfs(x, y, S ^ pre[x][y][S][2]); } int main() { scanf("%d%d", &n, &m); memset(dp, 0x3f3f, sizeof(dp)); bin[0] = 1; for(int i = 1; i <= 10; ++i) bin[i] = bin[i - 1] << 1; for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) { scanf("%d", &a[i][j]); if(!a[i][j]) dp[i][j][bin[p++]] = 0; } for(int S = 1; S < bin[p]; ++S) { queue<int> q; for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) for(int S0 = S; S0; S0 = (S0 - 1) & S) { int tmp = dp[i][j][S ^ S0] + dp[i][j][S0] - a[i][j]; if(tmp < dp[i][j][S]) { dp[i][j][S] = tmp; pre[i][j][S][0] = i; pre[i][j][S][1] = j; pre[i][j][S][2] = S0; } if(dp[i][j][S] < inf) { t[i][j][S] = 1; q.push(i); q.push(j); q.push(S); } } while(!q.empty()) { int x = q.front(); q.pop(); int y = q.front(); q.pop(); int S = q.front(); q.pop(); t[x][y][S] = 0; for(int i = 0; i < 4; ++i) { int xx = x + dx[i], yy = y + dy[i]; if(xx > 0 && xx <= n && yy > 0 && yy <= m && dp[xx][yy][S] > dp[x][y][S] + a[xx][yy]) { dp[xx][yy][S] = dp[x][y][S] + a[xx][yy]; pre[xx][yy][S][0] = x; pre[xx][yy][S][1] = y; pre[xx][yy][S][2] = S; if(!t[xx][yy][S]) { t[xx][yy][S] = 1; q.push(xx); q.push(yy); q.push(S); } } } } } for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) if(!a[i][j]) { printf("%d\n", dp[i][j][bin[p] - 1]); dfs(i, j, bin[p] - 1); for(int x = 1; x <= n; ++x) { for(int y = 1; y <= m; ++y) { if(a[x][y]) printf("%c", vis[x][y] ? 'o' : '_'); else printf("x"); } puts(""); } return 0; } return 0; }