[WC2008]游览计划(斯坦纳树裸题)

https://www.luogu.com.cn/problem/P4294

斯坦纳树做什么?

一个图,其中有\(k\)个关键点,选一个生成树把这些关键点连起来,求最小的生成树边权和。

\(k\)通常比较小。

斯坦纳树怎么做?

\(f[i][S]\)为,现在有一棵以\(i\)为根的生成树,\(S\in[0,2^k)\)表示连接了哪些关键点,的最小生成树边权和。

转移有两种:
1.枚举\(t∈S\),\(f[i][S]=min(f[i][S],f[i][t]+f[i][S^t]-(i的点权(如果有的话)))\)
2.枚举\(i\)的相邻点\(j\),\(f[i][S]=min(f[i][S],f[j][S]+(j->i的边权)+(i的点权))\)

感性理解这样一定可以转移到每一棵生成树。

实现:

注意到第二个转移有环,所以需要最短路算法,如果把每个\(f[i][S]\)都视作一个点,跑最短路,又不好处理第一个转移。

考虑分层图,以S分层,从小到大枚举S。

每一层开始前先做第一个转移,因为第一个转移一定是由低层转移来的。

接着第二个转移,第二个转移一定是在同一层之间转移,用最短路优化即可。

时间复杂度:
\(O(n*3^k+2^k*(m+n)*log~m)\)

这题:

还需要给出方案,对每一个点记录它是由谁转移而来,倒退回去即可。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int N = 12;

int a2[N];

int n, m, a[N][N];
int id[N][N], id0;

void Init() {
	a2[0] = 1; fo(i, 1, 10) a2[i] = a2[i - 1] * 2;
	scanf("%d %d", &n, &m);
	fo(i, 1, n) fo(j, 1, m) scanf("%d", &a[i][j]);
	fo(i, 1, n)	fo(j, 1, m) if(!a[i][j])
		id[i][j] = ++ id0;
}

int mov[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

const int M = 1 << 10;

const int inf = 1e9;

struct P {
	int x, y, v;
};

bool operator < (P a, P b) { return a.v > b.v;}

priority_queue<P> q;

int bz[N][N];
int f[N][N][M];
int fr[N][N][M][3];

void work() {
	fo(i, 1, n) fo(j, 1, m) ff(s, 1, a2[id0]) f[i][j][s] = inf;
	fo(i, 1, n) fo(j, 1, m) {
		if(id[i][j]) f[i][j][a2[id[i][j] - 1]] = 0;
		f[i][j][0] = a[i][j];
	}
	ff(s, 0, a2[id0]) {
		fo(i, 1, n) fo(j, 1, m) {
			for(int t = (s - 1) & s; t; t = (t - 1) & s) {
				int v = f[i][j][t] + f[i][j][s ^ t] - a[i][j];
				if(v < f[i][j][s]) {
					f[i][j][s] = v;
					fr[i][j][s][0] = i;
					fr[i][j][s][1] = j;
					fr[i][j][s][2] = t;
				}
			}
		}
		fo(i, 1, n) fo(j, 1, m) {
			bz[i][j] = 0;
			q.push((P) {i, j, f[i][j][s]});
		}
		while(q.size()) {
			P b = q.top(); q.pop();
			if(bz[b.x][b.y]) continue;
			bz[b.x][b.y] = 1;
			fo(k, 0, 3) {
				int l = b.x + mov[k][0], r = b.y + mov[k][1];
				if(l && r && l <= n && r <= m) {
					ll v = b.v + a[l][r];
					if(v < f[l][r][s]) {
						f[l][r][s] = v;
						q.push((P) {l, r, v});
						fr[l][r][s][0] = b.x;
						fr[l][r][s][1] = b.y;
						fr[l][r][s][2] = s;
					}
				}
			}
		}
	}
}

int cho[N][N];

void dg(int x, int y, int s) {
	cho[x][y] = 1;
	if(!fr[x][y][s][0]) return;
	if(fr[x][y][s][2] == s) {
		dg(fr[x][y][s][0], fr[x][y][s][1], s);
	} else {
		dg(x, y, fr[x][y][s][2]);
		dg(x, y, s ^ fr[x][y][s][2]);
	}
}

void End() {
	int ans = inf, x, y;
	fo(i, 1, n)	fo(j, 1, m) {
		ll v = f[i][j][a2[id0] - 1];
		if(v < ans) {
			ans = v;
			x = i, y = j;
		}
	}
	pp("%d\n", ans);
	dg(x, y, a2[id0] - 1);
	fo(i, 1, n) {
		fo(j, 1, m) {
			if(!a[i][j]) pp("x"); else
				pp("%c", cho[i][j] ? 'o' : '_');
		}
		hh;
	}
}

int main() {
	Init();
	work();
	End();
}
posted @ 2020-03-15 23:14  Cold_Chair  阅读(152)  评论(0编辑  收藏  举报