[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;
View Code

以上是关于[WC2008] 游览计划的主要内容,如果未能解决你的问题,请参考以下文章

Bzoj2595: [Wc2008]游览计划

[WC2008]游览计划

BZOJ 25952595: [Wc2008]游览计划 (状压DP+spfa,斯坦纳树?)

LuoGuP4294:[WC2008]游览计划

[WC2008] 游览计划

BZOJ_2595_[Wc2008]游览计划_斯坦纳树