BZOJ2595 [Wc2008]游览计划 【状压dp + 最短路】
题目链接
题解
著名的斯坦纳树问题
设\(f[i][j][s]\)表示点\((i,j)\)与景点联通状况为\(s\)的最小志愿者数
设\(val[i][j]\)为\((i,j)\)需要的志愿者数
有两种转移
一种是自己转移
\[f[i][j][s] = min\{f[i][j][e] + f[i][j][\complement_s e] - val[i][j]\}
\]
一种是由周围转移过来
\[f[i][j][s] = min\{f[i][j][s] + f[x][y][s] + dis\}
\]
第一种\(O(3^{K})\)枚举子集,第二种就是最短路
纪念一下BZOJ500题,截个图,,数字挺整的
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
using namespace std;
const int maxn = 11,maxm = 1 << 10,INF = 1000000000;
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
return out * flag;
}
int n,m,K,maxv;
int f[maxn][maxn][maxm],val[maxn][maxn],id[maxn][maxn],vis[maxn][maxn];
int head,tail,X[4] = {0,0,-1,1},Y[4] = {-1,1,0,0};
int S[maxn][maxn];
cp q[10000];
struct tri{
int x,y,s;
};
vector<tri> pre[maxn][maxn][maxm];
void spfa(int s){
head = 0; tail = -1;
REP(i,n) REP(j,m) q[++tail] = mp(i,j),vis[i][j] = true;
cp u; int nx,ny;
while (head <= tail){
u = q[head++];
vis[u.first][u.second] = false;
for (int k = 0; k < 4; k++){
nx = u.first + X[k];
ny = u.second + Y[k];
if (nx < 1 || ny < 1 || nx > n || ny > m) continue;
if (f[nx][ny][s] > f[u.first][u.second][s] + val[nx][ny]){
f[nx][ny][s] = f[u.first][u.second][s] + val[nx][ny];
pre[nx][ny][s].clear();
pre[nx][ny][s].push_back((tri){u.first,u.second,s});
if (!vis[nx][ny]) q[++tail] = mp(nx,ny);
}
}
}
}
void dfs(int x,int y,int s){
S[x][y] = true;
tri u;
for (unsigned int i = 0; i < pre[x][y][s].size(); i++){
u = pre[x][y][s][i];
dfs(u.x,u.y,u.s);
}
}
void work(){
maxv = (1 << K) - 1;
memset(f,0x3f3f3f3f,sizeof(f));
REP(i,n) REP(j,m){
f[i][j][0] = 0;
if (!val[i][j]) f[i][j][1 << id[i][j] - 1] = 0;
}
for (int s = 0; s <= maxv; s++){
REP(i,n) REP(j,m){
for (int e = s; e; e = (e - 1) & s){
if (f[i][j][s] > f[i][j][e] + f[i][j][s ^ e] - val[i][j]){
f[i][j][s] = f[i][j][e] + f[i][j][s ^ e] - val[i][j];
pre[i][j][s].clear();
pre[i][j][s].push_back((tri){i,j,e});
pre[i][j][s].push_back((tri){i,j,s ^ e});
}
}
}
spfa(s);
}
int ans = INF,x,y;
REP(i,n) REP(j,m) if (ans > f[i][j][maxv]) ans = f[i][j][maxv],x = i,y = j;
printf("%d\n",ans);
dfs(x,y,maxv);
REP(i,n){
REP(j,m){
if (id[i][j]) putchar('x');
else if (S[i][j]) putchar('o');
else putchar('_');
}
puts("");
}
}
int main(){
n = read(); m = read();
REP(i,n) REP(j,m){
val[i][j] = read();
if (!val[i][j]) id[i][j] = ++K;
}
work();
return 0;
}