[WC2008] 游览计划
Posted pedesis
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[WC2008] 游览计划相关的知识,希望对你有一定的参考价值。
题意
给定一个 $N \times M$ 的网格图,每个格子与四个方向上相邻的格子联通,其中有 $K$ 个点是景点。非景点格子有正点权 $A_i,j$,你需要选择一些格子使得所有景点联通,求最小点权和并输出选点方案。
数据范围
$N,M,K \leq 10$
分析
我怀疑我在水博客,但是我没有证据
这很显然是个最小斯坦纳树,经典例题裸题
设 $f[i][j][s]$ 表示以点 $(i,j)$ 为根,点集 $s$ 都在树上的生成树最小点权
于是有两个状态转移方程 $$f[i][j][s]=min_k \subsetneq s\f[i][j][k]+f[i][j][s-k]-a[i][j]\$$ $$f[i][j][k]=min_\vert i‘-i \vert + \vert j‘-j \vert =1\f[i‘][j‘][s]+a[i][j]\$$
但由于题目要求输出选点方案,所以我们要记录每个状态最后被更新时的前继状态,以便最后返回遍历所有用过的点
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <queue> #include <set> using namespace std; #define ll long long #define inf 0x3f3f3f3f #define N 11 int n, m, p, ok, x0, y0; int g[N][N], f[N][N][1 << N], vis[N][N]; int d[4][2] = 0, 1, 1, 0, 0, -1, -1, 0; queue<pair<int, int> > q; struct Point int x, y, s; pre[N][N][1 << N], tmp; void spfa(int s) while (!q.empty()) int x = q.front().first, y = q.front().second; q.pop(); vis[x][y] = 0; for (int i = 0; i < 4; i++) int dx = x + d[i][0], dy = y + d[i][1]; if (dx < 1 || dx > n || dy < 1 || dy > m) continue; if (f[dx][dy][s] > f[x][y][s] + g[dx][dy]) f[dx][dy][s] = f[x][y][s] + g[dx][dy]; tmp.x = x; tmp.y = y; tmp.s = s; pre[dx][dy][s] = tmp; if (!vis[dx][dy]) vis[dx][dy] = 1, q.push(make_pair(dx, dy)); void dfs(int x, int y, int s) vis[x][y] = 1; tmp = pre[x][y][s]; if (!tmp.s) return; dfs(tmp.x, tmp.y, tmp.s); tmp = pre[x][y][s]; if (tmp.x == x && tmp.y == y) dfs(x, y, s ^ tmp.s); int main() scanf("%d%d", &n, &m); memset(f, 0x3f, sizeof f); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) scanf("%d", &g[i][j]); if (!g[i][j]) f[i][j][1 << p] = 0, p++; for (int s = 1; s < (1 << p); s++) for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) for (int k = s & (s - 1); k; k = (k - 1) & s) if (f[i][j][s] > f[i][j][k] + f[i][j][s ^ k] - g[i][j]) f[i][j][s] = f[i][j][k] + f[i][j][s ^ k] - g[i][j]; tmp.x = i; tmp.y = j; tmp.s = k; pre[i][j][s] = tmp; if (f[i][j][s] < inf) vis[i][j] = 1, q.push(make_pair(i, j)); spfa(s); for (int i = 1; i <= n && !ok; i++) for (int j = 1; j <= m && !ok; j++) if (!g[i][j]) x0 = i, y0 = j, ok = 1; printf("%d\n", f[x0][y0][(1 << p) - 1]); dfs(x0, y0, (1 << p) - 1); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) if (!g[i][j]) printf("x"); else if (vis[i][j]) printf("o"); else printf("_"); printf("\n"); return 0;
以上是关于[WC2008] 游览计划的主要内容,如果未能解决你的问题,请参考以下文章