zoukankan      html  css  js  c++  java
  • [WC2008] 游览计划

    题目链接

    题意

      给定一个 $N imes 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
    ", 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("
    ");
        }
    
        return 0;
    }
    View Code
  • 相关阅读:
    flash
    Python
    ArchLinux2012.12后续软件安装
    archlinux win7+ubuntu双系统引导问题
    转盘项目
    Archlinux121210+kde4.95声音输出解决
    作为程序员为什么一直都很努力,却没有进步?
    打造属于自己的谷歌地图版博客公告【演示+源码】
    人类已经不能阻止开源了Web 2.0开源应用大汇总
    LAMP网站架构方案分析【精辟转】
  • 原文地址:https://www.cnblogs.com/Pedesis/p/11378857.html
Copyright © 2011-2022 走看看