题目链接

Luogu 4294

(我做这道题的时候BZOJ全站的SPJ都炸了 提交秒WA 幸好有洛谷)

题解

这道题是【斯坦纳树】的经典例题。斯坦纳树是这样一类问题:带边权无向图上有几个(一般约10个)点是【关键点】,要求选择一些边使这些点在同一个联通块内,同时要求所选的边的边权和最小。

怎么解决斯坦纳树问题?……其实,就是一种状压DP。

\(dp[i][j]\)表示以i号节点为根,当前状态为j(j的二进制中已经与i连通的点对应位置为1)。

这个“以i为根”是哪来的呢?其实i可以是联通块中任意一个点,没有额外限制,只是引入这个i就可以DP了。

当根i不改变时(即合并两个都包含i的联通块)状态转移方程是:

\[dp[i][j] = \min_{s \in j}\{dp[i][s] + dp[i][\complement_js] - val[i]\}
\]

(\(val[i]\)表示本题中i号点的权值,减去一个是因为\(dp[i][s]\)和\(dp[i][\complement_js]\)中都含有i号点的权值,要防止“加重了”)

当根改变时(即在原有联通块中加入一个新节点i并设置为根,要求i、k相邻):

\[dp[i][j] = \min\{dp[k][j] + val[i]\}
\]

第一个状态转移方程有顺序,可以直接DP;而第二个状态转移方程没有明显顺序,但可以按照最短路的SPFA算法“DP”(神奇!)。

代码

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <queue>
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;
typedef long long ll;
template <class T>
void read(T &x){
char c;
bool op = 0;
while(c = getchar(), c < '0' || c > '9')
if(c == '-') op = 1;
x = c - '0';
while(c = getchar(), c >= '0' && c <= '9')
x = x * 10 + c - '0';
if(op) x = -x;
}
template <class T>
void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x >= 10) write(x / 10);
putchar('0' + x % 10);
} const int INF = 0x3f3f3f3f;
int n, m, K, root, f[101][1111], a[101], ans[11][11];
bool inq[101];
typedef pair<int, int> par;
typedef pair<par, int> rec;
#define fi first
#define se second
#define mp make_pair
#define num(u) (u.fi * m + u.se)
rec pre[101][1111];
const int dx[] = {0, 0, -1, 1};
const int dy[] = {1, -1, 0, 0};
queue<par> que; bool legal(par u){
return u.fi >= 0 && u.se >= 0 && u.fi < n && u.se < m;
}
void spfa(int now){
while(!que.empty()){
par u = que.front();
que.pop();
inq[num(u)] = 0;
for(int d = 0; d < 4; d++){
par v = mp(u.fi + dx[d], u.se + dy[d]);
int nu = num(u), nv = num(v);
if(legal(v) && f[nv][now] > f[nu][now] + a[nv]){
f[nv][now] = f[nu][now] + a[nv];
if(!inq[nv]) inq[nv] = 1, que.push(v);
pre[nv][now] = mp(u, now);
}
}
}
}
void dfs(par u, int now){
if(!pre[num(u)][now].se) return;
ans[u.fi][u.se] = 1;
int nu = num(u);
if(pre[nu][now].fi == u) dfs(u, now ^ pre[nu][now].se);
dfs(pre[nu][now].fi, pre[nu][now].se);
} int main(){ read(n), read(m);
memset(f, 0x3f, sizeof(f));
for(int i = 0, tot = 0; i < n; i++)
for(int j = 0; j < m; j++){
read(a[tot]);
if(!a[tot]) f[tot][1 << (K++)] = 0, root = tot;
tot++;
}
for(int now = 1; now < (1 << K); now++){
for(int i = 0; i < n * m; i++){
for(int s = now & (now - 1); s; s = now & (s - 1))
if(f[i][now] > f[i][s] + f[i][now ^ s] - a[i]){
f[i][now] = f[i][s] + f[i][now ^ s] - a[i];
pre[i][now] = mp(mp(i / m, i % m), s);
}
if(f[i][now] < INF)
que.push(mp(i / m, i % m)), inq[i] = 1;
}
spfa(now);
}
write(f[root][(1 << K) - 1]), enter;
dfs(mp(root / m, root % m), (1 << K) - 1);
for(int i = 0, tot = 0; i < n; i++){
for(int j = 0; j < m; j++)
if(!a[tot++]) putchar('x');
else putchar(ans[i][j] ? 'o' : '_');
enter;
} return 0;
}

Luogu 4294 [WC2008]游览计划 | 斯坦纳树的更多相关文章

  1. 洛谷4294 [WC2008]游览计划——斯坦纳树

    题目:https://www.luogu.org/problemnew/show/P4294 大概是状压.两种转移,一个是以同一个点为中心,S由自己的子集拼起来:一个是S相同.中心不同的同层转移. 注 ...

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

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

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

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

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

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

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

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

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

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

  7. P4294 [WC2008]游览计划 (斯坦纳树)

    题目链接 差不多是斯坦纳树裸题,不过边权化成了点权,这样在合并两棵子树时需要去掉根结点的权值,防止重复. 题目还要求输出解,只要在转移时记录下路径,然后dfs一遍就好了. #include<bi ...

  8. 【BZOJ-2595】游览计划 斯坦纳树

    2595: [Wc2008]游览计划 Time Limit: 10 Sec  Memory Limit: 256 MBSec  Special JudgeSubmit: 1518  Solved: 7 ...

  9. luogu P4294 [WC2008]游览计划

    LINK:游览计划 斯坦纳树例题. 斯坦纳树是这样一类问题:带权无向图上有K个关键点 求出包含这K个点的最小生成树. 也就是说 求最小生成树 但是 并不是整张图 仅限于K个点. 可以发现我们利用克鲁斯 ...

随机推荐

  1. Part 6:静态文件--Django从入门到精通系列教程

    该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. 前面我们编写了一个经过测试的投票应用,现在让 ...

  2. (一)ABP添加控制器和页面(有时候页面不出来)

    1:添加控制器后需要写[Area("AppAreaName")] 2:继承  WebControllerBase 3:创建视图就可以出现index页面了

  3. FFMPEG指令

    FFmpeg是一个用于音视频处理的自由软件,被广泛用于音视频开发.FFmpeg功能强大,本文主要介绍如何使用FFmpeg命令行工具进行简单的视频处理. 安装FFmpeg可以在官网下载各平台软件包或者静 ...

  4. db2修改最大连接数

    查看当前连接数,sample为数据库名db2 list applications for db sample db2 list applications for db sample show deta ...

  5. jenkins 上命令各种找不到问题

    代码: 兵马未动,粮草先行 作者: 传说中的汽水枪 如有错误,请留言指正,欢迎一起探讨. 转载请注明出处.   在使用jenkins的时候,会使用一些命令行,有的时候明明在电脑的命令行(终端)可以执行 ...

  6. Daily scrum 12.24

    平安夜闲得想来一遍scrum,添加了之前ui组的数据库问题修复任务. 其实是之前忘记在任务中添加了.现在基本修复完成. Member Today’s task 林豪森 与学霸其他小组交流,处理整合问题 ...

  7. 【个人博客作业II】有关代码规范问题的讨论

    参考课程辅导书<构建之法>可以知道,程序的代码规范常指代码风格规范和代码设计规范两个方面,其中:代码风格规范包括(缩进,行宽,括号,断行与空白行,分行,命名,下划线,大小写,注释这几个部分 ...

  8. 同步手绘板——android端下笔后颜色变化

    实现效果 : 在设计之初我们以为是改变笔线条的粗细来实现类似效果,后来通过找其他相关软件比对发现是不改变线条的粗细,通过改变透明度实现下笔后颜色的渐变,虽然已实现,但渐变效果很一般,不流畅,算法还待优 ...

  9. 软件分析之QQ

    腾讯QQ(简称“QQ”)是腾讯公司开发的一款基于Internet的即时通信软件.腾讯QQ支持在线聊天.视频通话.点对点断点续传文件.共享文件.网络硬盘.自定义面板.QQ邮箱等多种功能,并可与多种通讯终 ...

  10. Sprint 冲刺第三阶段第6-10天

    这几天一直都在整理我们之前的内容,检查会不会有细节问题.例如界面跳转.颜色等. 因为一直没办法找到guitub存放位置.于是在这里存放一些主代码. MainActivity.java package ...