[WC2008]游览计划 「斯坦那树模板」

斯坦那树

百度释义

斯坦纳树问题是组合优化问题,与最小生成树相似,是最短网络的一种。最小生成树是在给定的点集和边中寻求最短网络使所有点连通。而最小斯坦纳树允许在给定点外增加额外的点,使生成的最短网络开销最小。

即最小斯坦那树即为并非选择所有的结点,而是选择一部分结点,为保证它们连通,且求解最小开销

题解

斯坦那树模板

发现直接表示点的存在性没有意义

设函数 \(f[i][state]\) 表示:对于点 \(i\),其它结点与其连通情况

那么有两种转移

其一、由其子集转移

\[f[i][state] = \min\limits_{sub \in state} \{f[i][sub] + f[i][\complement_{state}sub] - value_i\}\]

之所以要减去 \(value_i\) 是因为会算重

附:枚举子集的方法

for (int sub = state & (state - 1); sub; sub = (sub - 1) & state)

其二、由相邻当前状态下结点转移

\[f[i][state] = \min\limits_{state_p = true} \{f[p][state] + value_i\}\]

发现很像三角形不等式,故考虑 \(SPFA\) 转移

总复杂度 \(O (n3^n + kE2^n)\),\(3^n\) 为枚举子集总复杂度

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>

using namespace std;

const int MAXN = 10 + 5;
const int MAXM = 1 << 10;

const int INF = 0x3f3f3f3f;

const int NextX[4]= {- 1, 0, 0, 1}, NextY[4]= {0, - 1, 1, 0};

int N, M;
int Map[MAXN][MAXN]= {0};

struct preSt {
    int x, y;
    int state;

    preSt (int fx = 0, int fy = 0, int fs = 0) :
        x (fx), y (fy), state (fs) {}
} ;

int f[MAXN][MAXN][MAXM];
preSt pre[MAXN][MAXN][MAXM];
int cnt = 0;

queue<pair<int, int> > que;
void SPFA (int state) {
    while (! que.empty()) {
        pair<int, int> top = que.front();
        que.pop();

        int x = top.first, y = top.second;
        for (int i = 0; i < 4; i ++) {
            int tx = x + NextX[i];
            int ty = y + NextY[i];
            if (tx < 1 || tx > N || ty < 1 || ty > M)
                continue;
            if (f[x][y][state] + Map[tx][ty] < f[tx][ty][state]) {
                f[tx][ty][state] = f[x][y][state] + Map[tx][ty];
                pre[tx][ty][state] = preSt (x, y, state);
                que.push(make_pair (tx, ty));
            }
        }
    }
}

int tag[MAXN][MAXN]= {0};
void traceback (int x, int y, int state) {
    if (! x || ! y)
        return ;
    tag[x][y] = 1;
    preSt pr = pre[x][y][state];
    traceback (pr.x, pr.y, pr.state);
    if (pr.x == x && pr.y == y)
        traceback (pr.x, pr.y, state - pr.state);
}

int getnum () {
    int num = 0;
    char ch = getchar ();

    while (! isdigit (ch))
        ch = getchar ();
    while (isdigit (ch))
        num = (num << 3) + (num << 1) + ch - '0', ch = getchar ();

    return num;
}

int main () {
    memset (f, 0x3f, sizeof (f));
    N = getnum (), M = getnum ();
    int px, py;
    for (int i = 1; i <= N; i ++)
        for (int j = 1; j <= M; j ++) {
            Map[i][j] = getnum ();
            if (! Map[i][j]) {
                cnt ++, f[i][j][1 << (cnt - 1)] = 0;
                px = i, py = j;
            }
        }
    int limit = (1 << cnt) - 1;
    for (int state = 1; state <= limit; state ++) {
        for (int i = 1; i <= N; i ++)
            for (int j = 1; j <= M; j ++) {
                for (int sub = state & (state - 1); sub; sub = (sub - 1) & state) // from subset
                    if (f[i][j][sub] + f[i][j][state - sub] - Map[i][j] < f[i][j][state]) {
                        f[i][j][state] = f[i][j][sub] + f[i][j][state - sub] - Map[i][j];
                        pre[i][j][state] = preSt (i, j, sub);
                    }
                if (f[i][j][state] < INF)
                    que.push(make_pair (i, j));
            }
        SPFA (state); // from other nodes
    }
    traceback (px, py, limit);
    printf ("%d\n", f[px][py][limit]);
    for (int i = 1; i <= N; i ++) {
        for (int j = 1; j <= M; j ++) {
            if (! Map[i][j])
                putchar ('x');
            else {
                tag[i][j] ? putchar ('o') : putchar ('_');
            }
        }
        puts ("");
    }

    return 0;
}

/*
4 4
0 1 1 0
2 5 5 1
1 5 5 1
0 1 1 0
*/

原文地址:https://www.cnblogs.com/Colythme/p/10328442.html

时间: 2024-11-01 04:06:55

[WC2008]游览计划 「斯坦那树模板」的相关文章

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

题意: 分析: 斯坦纳树裸题,有几个需要注意的地方 给出矩阵,不用自己建图,但枚举子集转移时会算两遍,需要减去当前点的权值 方案记录比较麻烦,两边的转移都需要记录,最后dfs找方案会比较容易理解 代码: #include <stdio.h> #include <string.h> #include <algorithm> #include <queue> using namespace std; #define N 110 #define LL long l

【BZOJ2595】【Wc2008】游览计划、斯坦纳树

题解:斯坦纳树,实现神马的在代码里面有还看得过去的注释. 代码: #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 15 #define inf 0x3f3f3f3f using namespace std; const int dx[]={0,0,1,-1}; const int dy[

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

2595: [Wc2008]游览计划 Time Limit: 10 Sec  Memory Limit: 256 MBSec  Special JudgeSubmit: 1572  Solved: 739 Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点:否则表示控制该方块至少需要的志愿者数目. 相邻的整数用 (若干个) 空格隔开,行首行末也可能有多余的空格. Output 由 N

【BZOJ2595】[Wc2008]游览计划 斯坦纳树

[BZOJ2595][Wc2008]游览计划 Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点:否则表示控制该方块至少需要的志愿者数目. 相邻的整数用 (若干个) 空格隔开,行首行末也可能有多余的空格. Output 由 N + 1行组成.第一行为一个整数,表示你所给出的方案中安排的志愿者总数目. 接下来 N行,每行M 个字符,描述方案中相应方块的情况: z  ‘_’(下划线)表示该

[Wc2008]游览计划

2595: [Wc2008]游览计划 Time Limit: 10 Sec  Memory Limit: 256 MBSec  Special Judge[Submit][Status][Discuss] Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点:否则表示控制该方块至少需要的志愿者数目. 相邻的整数用 (若干个) 空格隔开,行首行末也可能有多余的空格. Output 由 N

bzoj2595 [Wc2008]游览计划

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2595 [题解] 斯坦纳树模板题.学了一发斯坦纳树. 对于一般的斯坦纳树,是 给出一些点和一些关键点和边,要求选择权值和最小的连通块使得关键点连通. 那么一般我们用f(x,status)表示在x,状态为status的最小权值和. 本题我们采用f(i,j,status)表示在(i,j),状态为status的最小权值和. 一开始权值就是题目给的,如果是景点那么在对应的标号的status赋值即可.

HDU 4085 斯坦纳树模板题

Dig The Wells Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 971    Accepted Submission(s): 416 Problem Description You may all know the famous story "Three monks". Recently they find som

斯坦纳树模板

屌炸天阿什么东西都有 丢 //斯坦纳树模板 让k个点联通的最小生成树 复杂度 n*3^k #include<iostream> #include<cstring> #include<set> #include<map> #include<cmath> #include<stack> #include<queue> #include<deque> #include<list> #include<

BZOJ 2595 [Wc2008]游览计划 ——斯坦纳树

[题目分析] 斯坦纳树=子集DP+SPFA? 用来学习斯坦纳树的模板. 大概就是用二进制来表示树包含的点,然后用跟几点表示树的形态. 更新分为两种,一种是合并两个子集,一种是换根,换根用SPFA迭代即可. [代码] #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <set> #include <map> #include