话说挺早就写过斯坦纳树了,不过当时没怎么总结,也不是很理解……现在来个小结吧~

斯坦纳树就是包含给定点的最小生成树(个人理解权值应当为正)。

一般来讲,给定点的数目应该很小吧。。。于是我们可以用状压DP来解决。

需要2个方程:

f[st][i]表示连通性至少为st,且经过i点的最小距离

方程1.f[st][i] = Min{f[s][i] + f[st - s][i]}(s为st的子集)

方程2.f[st][i] = Min{f[st][j] + w(i,j)}(i,j之间有边相连)

这里大家可能会有疑问,为什么是两个方程?

因为单凭第一个方程转移是不够准确的,会出现重点的问题。不过也一定包含合法的转移,所以我们要通过第二个方程来转移。如果我没有理解错,这也就是为什么这种方法不能应用于有负权的图上。

第一个直接枚举子集,完了以后用SPFA转移下一个(当然也可以用floyed)。

void SPFA(int sta)
{
while (!q.empty()) {
int i,j;
unpack(q.front(),i,j);
inq[q.front()] = 0;q.pop();
for (int k = 0; k < 4; ++k) {
int x = d[k][0] + i,y = d[k][1] + j,tmp;
if (x == -1 || y == -1 || x == n || y == m) continue;
if (update(x,y,sta,i,j,sta,f[i][j][sta] + a[x][y]) && !inq[tmp = pack(x,y)])
q.push(tmp),inq[tmp] = 1;
}
}
}

要记住f[st][i]表示连通性至少为st,是至少。

    for (int sta = 1,tmp; sta < Max_s; ++sta) {
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j) {
for (int s = sta&(sta - 1); s; s = (s - 1)&sta)
update(i,j,sta,i,j,s,f[i][j][s] + f[i][j][sta - s] - a[i][j]);
if (f[i][j][sta] != INF) q.push(tmp = pack(i,j)),inq[tmp] = 1;
}
SPFA(sta);
}

这样转移就可以了。

/**************************************************************
    Problem: 2595
    User: lazycal
    Language: C++
    Result: Accepted
    Time:144 ms
    Memory:1640 kb
****************************************************************/
 
#include <cstdio>
#include <cstring>
#include <queue>
using std::queue;
queue<int>q;
const int N = 10,INF = 0xf0f0f0f;
const int d[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
int f[N][N][1<<N],n,m,a[N][N],st[N][N],K,pre[N][N][1<<N];
bool vis[N][N],inq[N*N];
int pack(const int x,const int y){return x*10 + y;}
int pack2(const int x,const int y,const int s){return x*100000 + y*10000 + s;}
void unpack(const int x,int &i,int &j){i = x/10; j = x%10;}
void unpack2(const int x,int &i,int &j,int &s){s = x%10000; j = (x/10000)%10; i = x/100000;}
bool update(const int x,const int y,const int news,const int i,const int j,const int sta,const int w)
{
    if (f[x][y][news] > w) return f[x][y][news] = w,pre[x][y][news] = pack2(i,j,sta),true;
    return false;
}
void SPFA(int sta)
{
    while (!q.empty()) {
        int i,j;
        unpack(q.front(),i,j);
        inq[q.front()] = 0;q.pop();
        for (int k = 0; k < 4; ++k) {
            int x = d[k][0] + i,y = d[k][1] + j,tmp;
            if (x == -1 || y == -1 || x == n || y == m) continue;
            if (update(x,y,sta/*|st[x][y]*/,i,j,sta,f[i][j][sta] + a[x][y]) /*&& (sta|st[x][y]) == sta*/ && !inq[tmp = pack(x,y)])
                q.push(tmp),inq[tmp] = 1;
        }
    }
}
void dfs(const int i,const int j,const int s)
{
    if (!pre[i][j][s]) return;
    int x,y,ns;
    vis[i][j] = 1;
    unpack2(pre[i][j][s],x,y,ns);
    dfs(x,y,ns);
    if (x == i && y == j) dfs(x,y,s - ns);
}
void output()
{
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j)
            if (!a[i][j]) printf("x");
            else if (vis[i][j]) printf("o");
            else printf("_");
        puts("");
    }
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("2595.in","r",stdin);
    freopen("2595.out","w",stdout);
#endif
    scanf("%d%d",&n,&m);
    memset(f,0xf,sizeof f);
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < m; ++j) {
            scanf("%d",&a[i][j]);
            if (!a[i][j]) st[i][j] = 1<<(K++),f[i][j][st[i][j]] = 0;
        }
    int Max_s = (1 << K);
    for (int sta = 1,tmp; sta < Max_s; ++sta) {
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < m; ++j) {
                //if (a[i][j] && !(st[i][j]&sta)) continue;
                for (int s = sta&(sta - 1); s; s = (s - 1)&sta)
                    update(i,j,sta,i,j,s,f[i][j][s] + f[i][j][sta - s] - a[i][j]);
                if (f[i][j][sta] != INF) q.push(tmp = pack(i,j)),inq[tmp] = 1;//test!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            }
        SPFA(sta);
        // printf("\n%d\n",sta);
        // for (int i = 0; i < n; ++i) {
        //  for (int j = 0; j < m; ++j) printf("%d ",f[i][j][sta]);
        //  puts("");
        // }
    }
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < m; ++j)
            if (!a[i][j]) {
                printf("%d\n",f[i][j][Max_s - 1]);
                dfs(i,j,Max_s - 1);
                output();
                return 0;
            }
}

  

斯坦纳树 [bzoj2595][wc2008]游览计划 题解的更多相关文章

  1. BZOJ2595 Wc2008 游览计划 【斯坦纳树】【状压DP】*

    BZOJ2595 Wc2008 游览计划 Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个 ...

  2. [bzoj2595][WC2008]游览计划/[bzoj5180][Baltic2016]Cities_斯坦纳树

    游览计划 bzoj-2595 wc-2008 题目大意:题目链接.题目连接. 注释:略. 想法:裸题求斯坦纳树. 斯坦纳树有两种转移方式,设$f[s][i]$表示联通状态为$s$,以$i$为根的最小代 ...

  3. BZOJ2595 WC2008游览计划(斯坦纳树)

    斯坦纳树板子题. 考虑状压dp,设f[i][j][S]表示当前在点(i,j)考虑转移,其所在的联通块包含的关键点集(至少)为S的答案. 转移时首先枚举子集,有f[i][j][S]=min{f[i][j ...

  4. bzoj2595 [Wc2008]游览计划——斯坦纳树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2595 今天刚学了斯坦纳树,还不太会,写一道题练习一下: 参考了博客:http://www.c ...

  5. bzoj2595: [Wc2008]游览计划 斯坦纳树

    斯坦纳树是在一个图中选取某些特定点使其联通(可以选取额外的点),要求花费最小,最小生成树是斯坦纳树的一种特殊情况 我们用dp[i][j]来表示以i为根,和j状态是否和i联通,那么有 转移方程: dp[ ...

  6. BZOJ2595: [Wc2008]游览计划(斯坦纳树,状压DP)

    Time Limit: 10 Sec  Memory Limit: 256 MBSec  Special JudgeSubmit: 2030  Solved: 986[Submit][Status][ ...

  7. BZOJ2595 [Wc2008]游览计划 【状压dp + 最短路】

    题目链接 BZOJ2595 题解 著名的斯坦纳树问题 设\(f[i][j][s]\)表示点\((i,j)\)与景点联通状况为\(s\)的最小志愿者数 设\(val[i][j]\)为\((i,j)\)需 ...

  8. BZOJ2595[WC2008]游览计划

    Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点:否则表示控制该方块至少需要的志愿者数 ...

  9. BZOJ2595:[Wc2008]游览计划——题解(插头dp)

    http://www.lydsy.com/JudgeOnline/problem.php?id=2595 Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行 ...

随机推荐

  1. Kafka 0.8 如何创建topic

    1. 操作命令 bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 3 --partitions ...

  2. Vue模板语法V-bind

    一.插值 1.文本 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://w ...

  3. dialog 菜单

    dialog 菜单 # 默认将所有输出用 stderr 输出,不显示到屏幕 使用参数 --stdout 可将选择赋给变量 # 退出状态 0正确 1错误 窗体类型 --calendar # 日历 --c ...

  4. Spark笔记之使用UDF(User Define Function)

    一.UDF介绍 UDF(User Define Function),即用户自定义函数,Spark的官方文档中没有对UDF做过多介绍,猜想可能是认为比较简单吧. 几乎所有sql数据库的实现都为用户提供了 ...

  5. 关于markdown文件插入图片遇到的小问题和解决办法

    今天用md文件时候发现需要插入图片,以前没做过,所以写下来分享下. 1.先在自己的github上建一个仓库,里面新建个img文件夹存放图片,怎么建仓库可以上网找资料,这里就不详细说明了.建好的仓库如下 ...

  6. Hibernate常用的Java数据类型映射到mysql和Oracle

    研究了常用的Java基本数据类型在mysql和oracle数据库的映射类型.这里使用的是包装类型做研究,一般在hibernate声明的时候最好不要用基本类型,因为数据库中的null空数据有可能映射为基 ...

  7. Java反射--基于ParameterizedType实现泛型类,参数化类型

    一.引子: 项目中使用Gson的反序列化将json转化成具体的对象,具体方法是: package com.google.gson;下的反序列化方法 public <T> T fromJso ...

  8. 【Python】Flask系列-URL和视图笔记

    1.学习目标 熟悉Flask相关知识. 熟悉web开发流程. 能独立开发Flask项目. 2.环境配置 Python虚拟环境安装 因为python的框架更新迭代太快了,有时候需要在电脑上存在一个框架的 ...

  9. Linux监控重要进程的实现方法

    Linux监控重要进程的实现方法 不管后台服务程序写的多么健壮,还是可能会出现core dump等程序异常退出的情况,但是一般情况下需要在无 人为干预情况下,能够自动重新启动,保证服务进程能够服务用户 ...

  10. poj1033

    模拟题,注意不需要移动的情况要特殊输出 #include <cstdio> #include <cstring> #include <cstdlib> using ...