上个月写的题qwq……突然想写篇博客

题目:

洛谷4294

分析:

斯坦纳树模板题。

简单来说,斯坦纳树问题就是给定一张有边权(或点权)的无向图,要求选若干条边使图中一些选定的点连通(可以经过其他点),且边权(或点权)之和最小。很明显,这样最终形成的是一棵树。

通常,斯坦纳树问题规模都比较小。考虑状压DP。用\(dp[u][S]\)表示让点\(u\)与集合\(S\)中所有关键点连通的最小花费。有如下两种转移:

第一,把两条到\(u\)的路径拼在一起,减去重合点\(u\)的点权,即(\(w_u\)表示点\(u\)的点权,\(S'\)表示\(S\)的一个真非空子集,\(S-S'\)表示以\(S'\)相对于\(S\)的补集,下同):

\[dp[u][S]=min(dp[u][S']+dp[u][S-S']-w_u)
\]

第二,延伸一条路径,即(\(v\)与\(u\)之间存在一条边):

\[dp[u][S]=min(dp[v][S]+w_u)
\]

第二种存在循环更新的问题。但是它长得很像最短路,于是大力跑最短路算法即可。

注意更新顺序,要从小到大枚举集合\(S\),先更新第一种再更新第二种。

代码:

把上面的点\(u\)换成坐标就好了……

dp的时候记一下从哪个状态转移来的。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <queue>
#include <functional>
using namespace std;
#define _ 0 namespace zyt
{
template<typename T>
inline void read(T &x)
{
char c;
bool f = false;
x = 0;
do
c = getchar();
while (c != '-' && !isdigit(c));
if (c == '-')
f = true, c = getchar();
do
x = x * 10 + c - '0', c = getchar();
while (isdigit(c));
if (f)
x = -x;
}
template<typename T>
inline void write(T x)
{
static char buf[20];
char *pos = buf;
if (x < 0)
putchar('-'), x = -x;
do
*pos++ = x % 10 + '0';
while (x /= 10);
while (pos > buf)
putchar(*--pos);
}
const int N = 10, ST = 1 << N, INF = 0x3f3f3f3f;
struct _pre
{
int x, y, st;
_pre(const int _x = -1, const int _y = -1, const int _st = -1)
: x(_x), y(_y), st(_st) {}
}pre[N][N][ST];
struct point
{
int x, y;
point(const int _x = 0, const int _y = 0)
: x(_x), y(_y) {}
bool operator < (const point &b) const
{
return x == b.x ? y < b.y : x < b.x;
}
};
int f[N][N][ST], arr[N][N], n, m, k;
const int dx[] = {0, 0, 1, -1};
const int dy[] = {1, -1, 0, 0};
void Dijkstra(const int s)
{
typedef pair<int, point> pip;
static priority_queue<pip, vector<pip>, greater<pip> > q;
static bool vis[N][N];
while (!q.empty())
q.pop();
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
{
q.push(make_pair(f[i][j][s], point(i, j)));
vis[i][j] = false;
}
while (!q.empty())
{
point u = q.top().second;
q.pop();
vis[u.x][u.y] = true;
for (int i = 0; i < 4; i++)
{
point v = point(u.x + dx[i], u.y + dy[i]);
if (v.x < 0 || v.x >= n || v.y < 0 || v.y >= m || vis[v.x][v.y])
continue;
if (f[v.x][v.y][s] > f[u.x][u.y][s] + arr[v.x][v.y])
{
f[v.x][v.y][s] = f[u.x][u.y][s] + arr[v.x][v.y];
pre[v.x][v.y][s] = _pre(u.x, u.y, s);
q.push(make_pair(f[v.x][v.y][s], v));
}
}
}
}
bool mark[N][N];
void dfs(const _pre p)
{
mark[p.x][p.y] = true;
_pre nxt = pre[p.x][p.y][p.st];
if (nxt.x == -1 && nxt.y == -1 && nxt.st == -1)
return;
dfs(nxt);
if (nxt.st != p.st)
{
nxt.st ^= p.st;
dfs(nxt);
}
}
int work()
{
read(n), read(m);
for (int i = 0; i < n; i++)
{
memset(f[i], INF, sizeof(int[m][ST]));
for (int j = 0; j < m; j++)
{
read(arr[i][j]);
if (!arr[i][j])
f[i][j][1 << (k++)] = 0;
}
}
for (int i = 0; i < (1 << k); i++)
{
for (int j = (i - 1) & i; j; j = (j - 1) & i)
for (int x = 0; x < n; x++)
for (int y = 0; y < m; y++)
if (f[x][y][i] > f[x][y][j] + f[x][y][i ^ j] - arr[x][y])
{
f[x][y][i] = f[x][y][j] + f[x][y][i ^ j] - arr[x][y];
pre[x][y][i] = _pre(x, y, j);
}
Dijkstra(i);
}
int ans = INF;
_pre pans;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
if (ans > f[i][j][(1 << k) - 1])
{
ans = f[i][j][(1 << k) - 1];
pans = _pre(i, j, (1 << k) - 1);
}
write(ans), putchar('\n');
dfs(pans);
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
if (!arr[i][j])
putchar('x');
else if (mark[i][j])
putchar('o');
else
putchar('_');
putchar('\n');
}
return (0^_^0);
}
}
int main()
{
return zyt::work();
}

【BZOJ2595_洛谷4294】[WC2008]游览计划(斯坦纳树_状压DP)的更多相关文章

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

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

  2. Luogu 4294 [WC2008]游览计划 | 斯坦纳树

    题目链接 Luogu 4294 (我做这道题的时候BZOJ全站的SPJ都炸了 提交秒WA 幸好有洛谷) 题解 这道题是[斯坦纳树]的经典例题.斯坦纳树是这样一类问题:带边权无向图上有几个(一般约10个 ...

  3. [bzoj4006][JLOI2015]管道连接_斯坦纳树_状压dp

    管道连接 bzoj-4006 JLOI-2015 题目大意:给定一张$n$个节点$m$条边的带边权无向图.并且给定$p$个重要节点,每个重要节点都有一个颜色.求一个边权和最小的边集使得颜色相同的重要节 ...

  4. BZOJ 4006 Luogu P3264 [JLOI2015]管道连接 (斯坦纳树、状压DP)

    题目链接: (bzoj)https://www.lydsy.com/JudgeOnline/problem.php?id=4006 (luogu)https://www.luogu.org/probl ...

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. OpenCV+Python识别车牌和字符分割的实现

    本篇文章主要基于python语言和OpenCV库(cv2)进行车牌区域识别和字符分割,开篇之前针对在python中安装opencv的环境这里不做介绍,可以自行安装配置! 车牌号检测需要大致分为四个部分 ...

  2. FZU 1492 地震预测(模拟链表的应用)(Java实现)

    FZU 1492 地震预测(模拟链表的应用)(Java实现) 怀特先生是一名研究地震的科学家,最近他发现如果知道某一段时间内的地壳震动能量采样的最小波动值之和,可以有效地预测大地震的发生. 假设已知一 ...

  3. HYSBZ - 1050(旅行comf 并查集Java实现)

    HYSBZ - 1050(旅行comf Java实现) 原题地址 解法:枚举每一条边,对于这条边,我们需要找到集合中和其值相差最小的最大边,这个集合是指与包括i边在内的ST联通集.对于这一要求,我们只 ...

  4. Maven学习总结(29)——Maven项目的pom.xml中log4j2配置

        <dependency>         <groupId>org.apache.logging.log4j</groupId>         <a ...

  5. 模拟select控件功能

    直接上代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF ...

  6. MVC系统学习4—ModelMetaData

    在Mvc R2中,新引入了一些扩展方法,如后面带一个for的方法,这些扩展方法会根据Model的属性自定生成相应的Html元素,如Html.EditFor(Model=>Model.IsAppr ...

  7. 初次使用Let's encrypt

    wget --no-check-certificate -O shadowsocks.sh https://raw.githubusercontent.com/teddysun/shadowsocks ...

  8. lnmp的安装--nginx

    1.nginx的安装 安装所需环境 Nginx 是 C语言 开发,建议在 Linux 上运行,当然,也可以安装 Windows 版本,本篇则使用 CentOS 7 作为安装环境. 一. gcc 安装安 ...

  9. 【BZOJ2434】阿狸的打字机(fail树,DFS序)

    题意: 1<=N<=10^5 1<=M<=10^5 输入总长<=10^5   思路: From http://blog.csdn.net/lych_cys/article ...

  10. CF899F. Letters Removing

    给一个字符串支持以下操作:区间删除某个特定字符.最后输出字符串.n,m<=200000. 这题我居然不会可以回家了.. 首先,单点删除,选个平衡树比如set. 然后,他给的下标是会随删除操作变化 ...