【BZOJ】2595: [Wc2008]游览计划

题意

\(n * m\)的网格,如果\(a_{i, j} = 0\)则表示景点,否则表示这里的需要的志愿者人数。求一种安排志愿者的方案使得所有景点连通且志愿者最少。

分析

本题可以插头dp,然而有一个东西叫斯坦纳树,来学习学习。
\(f(i, j, s)\)表示\((i, j)\)为根,连通性为\(s\)的最少志愿者。则有转移:

$$ f(i, j, s) = min \begin{cases} f(i, j, t) + f(i, j, s-t) - a_{i, j} & t \neq \varnothing, t \subset s \\ f(i', j', s) & (i, j)与(i', j')相邻 \\ \end{cases} $$

第一个转移可以直接做,然而第二个转移不是\(dag\)= =,所以我们得用最短路来求出来。
所以我们状压dp一下,然后每一个状态\(s\)先更新了第一种转移,然后再最短路一下。
然后完了= =

题解

如分析。

#include <bits/stdc++.h>
using namespace std;
const int N=10, M=N*N+5, oo=0x3f3f3f3f, dx[]={1, -1, 0, 0}, dy[]={0, 0, 1, -1};
int n, m, a[N][N], f[N][N][1<<N], p[N][N][1<<N], go[N][N], q[M], ta, fr, tot;
bool vis[N][N], ok[N][N];
void init() {
	memset(f, 0x3f, sizeof f);
	memset(p, -1, sizeof p);
	scanf("%d%d", &n, &m);
	for(int i=0; i<n; ++i) {
		for(int j=0; j<m; ++j) {
			scanf("%d", &a[i][j]);
			if(!a[i][j]) {
				f[i][j][go[i][j]=1<<tot++]=0;
			}
		}
	}
}
inline int hash(const int x, const int y, const int s) {
	return y+x*100+s*10000;
}
void spfa(int s) {
	while(fr!=ta) {
		int h=q[fr++], y=h%100, x=h/100;
		fr=fr==M?0:fr;
		for(int k=0; k<4; ++k) {
			int fx=x+dx[k], fy=y+dy[k];
			if(fx<0 || fx>=n || fy<0 || fy>=m) {
				continue;
			}
			int temp=f[x][y][s]+a[fx][fy];
			if(f[fx][fy][s]>temp) {
				f[fx][fy][s]=temp;
				if(!vis[fx][fy]) {
					vis[fx][fy]=1;
					q[ta++]=hash(fx, fy, 0);
					ta=ta==M?0:ta;
				}
				p[fx][fy][s]=hash(x, y, s);
			}
		}
		vis[x][y]=0;
	}
	fr=ta=0;
}
void work() {
	int all=1<<tot;
	for(int s=1; s<all; ++s) {
		for(int i=0; i<n; ++i) {
			for(int j=0; j<m; ++j) {
				for(int t=s&(s-1); t; t=s&(t-1)) {
					int temp=f[i][j][t]+f[i][j][s-t]-a[i][j];
					if(f[i][j][s]>temp) {
						f[i][j][s]=temp;
						p[i][j][s]=hash(i, j, t);
					}
				}
				if(f[i][j][s]!=oo) {
					vis[i][j]=1;
					q[ta++]=hash(i, j, 0);
				}
			}
		}
		spfa(s);
	}
}
void dfs(int i, int j, int s) {
	ok[i][j]=1;
	int h=p[i][j][s];
	if(h<0) {
		return;
	}
	int y=h%100, x=(h%10000)/100, t=h/10000;
	dfs(x, y, t);
	if(i==x && j==y) {
		dfs(x, y, s-t);
	}
}
void out() {
	for(int i=0; i<n; ++i) {
		for(int j=0; j<m; ++j) {
			if(go[i][j]) {
				printf("%d\n", f[i][j][(1<<tot)-1]);
				dfs(i, j, (1<<tot)-1);
				return;
			}
		}
	}
}
void prin() {
	out();
	for(int i=0; i<n; ++i) {
		for(int j=0; j<m; ++j) {
			if(go[i][j]) {
				putchar('x');
			}
			else if(ok[i][j]) {
				putchar('o');
			}
			else {
				putchar('_');
			}
		}
		puts("");
	}
}
int main() {
	init();
	work();
	prin();
	return 0;
}
posted @ 2015-11-22 13:49  iwtwiioi  阅读(227)  评论(0编辑  收藏  举报