码迷,mamicode.com
首页 > 其他好文 > 详细

[WC2008] 游览计划

时间:2019-08-19 19:27:01      阅读:65      评论:0      收藏:0      [点我收藏+]

标签:http   cstring   hid   first   裸题   网格   struct   org   scan   

题目链接

题意

  给定一个 $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;
}
View Code

[WC2008] 游览计划

标签:http   cstring   hid   first   裸题   网格   struct   org   scan   

原文地址:https://www.cnblogs.com/Pedesis/p/11378857.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!