教练上次课讲了插头dp,然后列出的插头dp题目里有这道题。本来想练练插头dp的结果用更快更简单的斯坦纳树解决了这道题。
斯坦纳树经典题目。
斯坦纳树其实并不是很难,一般用来解决在一张无向图上选
( ext{m})个点使这( ext{m})个点连通并且始所选边边权最小。
那么回到本题。
设(f_{i,j,s})表示当前在第( ext{i})行( ext{j})列的点,状态为( ext{s})的时候最小权值和。设( ext{s1})代表( ext{s})的子集。
那么转移方程显而易见。
[f_{i,j,s} = min(f_{i,j,s},f_{i,s1xors} - a_{i,j})
]
这个转移应该不难看懂吧。
但是这里会出现一个问题:那就是会出现新的节点。
我们可以用最短路的方式求出下面一个方程:
设( ext{x,y})为当前的节点,( ext{xa,ya})是新拓展出的与( ext{x,y})相连接的节点。
则:
[f_{xa,ya,s} = min(f_{xa,ya,s},f_{x,y,s} +a_{xa,ya})
]
然后我们就顺利的解决了这道题。
My Code:
#include <bits/stdc++.h>
const int maxn = 20;
const int maxm = 20;
const int inf = 0x3f3f3f3f;
typedef long long ll;
struct Pair {
int x, y;
Pair() { x = 0; y = 0; }
Pair(int _x,int _y) { x = _x; y = _y; }
};
inline Pair Make_Pair(int x,int y) {
return Pair(x,y);
}
template<class T> inline void read(T& res) {
res = 0; bool sign = 0; char ch;
do ch = getchar(), sign |= ch == '-'; while(!isdigit(ch));
while(isdigit(ch)) res = (res << 1) + (res << 3) + (ch & 15), ch = getchar();
(sign) && (res = -res);
}
const int dir[4][2] = {{0,-1},{0,1},{-1,0},{1,0}};
int n, m, i, j, k, tx, ty;
int a[maxn][maxn], f[maxn][maxn][1 << 11];
bool inq[maxn][maxn], ans[maxn][maxn];
struct state {
int x, y, S;
state() { x = y = S = 0; }
state(int _x,int _y,int _S) { x = _x; y = _y; S = _S; }
} las[maxn][maxn][1 << 11];
std::queue<Pair> q;
inline void spfa(int s) {
while(!q.empty()) {
Pair qwq = q.front(); q.pop();
int x = qwq.x, y = qwq.y; inq[x][y] = 0;
for(int i = 0, xa, ya;i < 4;i++) {
xa = x + dir[i][0];
ya = y + dir[i][1];
if(xa < 1 || xa > n || ya < 1 || ya > m) continue;
if(f[x][y][s] + a[xa][ya] < f[xa][ya][s]) {
f[xa][ya][s] = f[x][y][s] + a[xa][ya];
if(!inq[xa][ya]) q.push(Make_Pair(xa,ya)), inq[xa][ya] = 1;
las[xa][ya][s] = state(x,y,s);
}
}
}
}
void dfs(int i,int j,int s) {
if(!las[i][j][s].S) return; ans[i][j] = 1;
state qwq = las[i][j][s];
int x = qwq.x, y = qwq.y, S = qwq.S;
dfs(x,y,S);
if(i == x && j == y) dfs(i,j,s ^ S);
}
int main() {
read(n); read(m);
int cnt = 0;
memset(f,0x3f,sizeof(f));
for(int i = 1;i <= n;i++) {
for(int j = 1;j <= m;j++) {
read(a[i][j]);
if(!a[i][j]) tx = i, ty = j, f[i][j][1 << cnt] = 0, cnt++;
}
}
for(int l = 1, liml = (1 << cnt) - 1;l <= liml;l++) {
while(!q.empty()) q.pop();
for(int i = 1;i <= n;i++) {
for(int j = 1;j <= m;j++) {
for(int k = l;k;k = l & (k - 1)) {
if(f[i][j][l] > f[i][j][k] + f[i][j][k ^ l] - a[i][j]) {
f[i][j][l] = f[i][j][k] + f[i][j][k ^ l] - a[i][j];
las[i][j][l] = state(i,j,k);
}
}
if(f[i][j][l] != inf) q.push(Make_Pair(i,j)), inq[i][j] = 1;
}
}
spfa(l);
}
printf("%d
",f[tx][ty][(1 << cnt) - 1]);
dfs(tx,ty,(1 << cnt) - 1);
for(int i = 1;i <= n;i++) {
for(int j = 1;j <= m;j++) {
if(!a[i][j]) { printf("x"); continue; }
ans[i][j] ? printf("o") : printf("_");
}
puts("");
}
return 0;
}