【题解】WC2008游览计划
写的第一道斯坦纳树的题目。斯坦纳树在信息学中的应用一般为:在\(n\)个点之间给定\(k\)条边并相应的边权,求在保证给定\(m\)个点联通的条件下的最小边权和。解决此类问题的方法即为SPFA + 状压DP。参考论文:姜碧野 《SPFA的优化与应用》
我们用\(dp[u][S]\)表示以u为根,联通状态为S(01状态)的最小权值。以u为根:最优解必然构成一棵最小生成树。那么有两种转移方式:
\(dp[u][S] = dp[u][k'] + dp[u][k''] - a[u] \left ( S = k' + k'' \right )\)
这一个可以理解为\(u\)点为树上的一个分叉点,由该点分别走向两侧,一侧联通\(k'\)集合,一侧联通\(k''\)集合,通过\(u\)点联通成\(S\)集合。这种转移比较好处理,只需枚举子集即可。
\(dp[u][S] = dp[v][S] + w[u][v]\)
这里是从\(u\)点走向了\(v\)点,联通了这两个集合,通过\(u -> v\)这条边来连接。这一种情况就比较复杂了:\(u\)可以转移到\(v\),\(v\)也可以转移到\(u\),但这个方程式却给了我们一点联想:好像很像是最短路中的松弛操作呀。其满足三角形不等式,虽然图中有环的存在,但最优解并不构成环。所以我们用SPFA来进行这一部分的DP。
在本题中,\(f[i][j][S]\)代表以点\(i, j\)为根,联通状态为\(S\)的最优权值。
#include <bits/stdc++.h>
using namespace std;
#define maxn 12
#define maxm (1 << 10) + 2
#define INF 1e9
int n, m, K, f[maxn][maxn][maxm];
int a[maxn][maxn], bin[maxn];
int dx[] = {, , , -}, dy[] = {, -, , };
bool vis[maxn][maxn]; struct node
{
int x, y;
node (int _x = , int _y = ) { x = _x, y = _y; }
};
queue <node> q; struct Path
{
int x, y, s;
Path (int _x = , int _y = , int _s = ) { x = _x, y = _y, s = _s; }
}path[maxn][maxn][maxm]; int read()
{
int x = , k = ;
char c;
c = getchar();
while(c < '' || c > '') { if(c == '-') k = -; c = getchar(); }
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x * k;
} void init()
{
bin[] = ;
for(int i = ; i < ; i ++) bin[i] = bin[i - ] << ;
} void SPFA(int S)
{
while(!q.empty())
{
int x = q.front().x, y = q.front().y;
vis[x][y] = , q.pop();
for(int k = ; k < ; k ++)
{
int X = x + dx[k], Y = y + dy[k];
if(X < || Y < || X > n || Y > m) continue;
if(f[X][Y][S] > f[x][y][S] + a[X][Y])
{
f[X][Y][S] = f[x][y][S] + a[X][Y];
path[X][Y][S] = Path(x, y, S);
if(!vis[X][Y]) q.push(node(X, Y)), vis[X][Y] = ;
}
}
}
} #define t path[x][y][S]
void dfs(int x, int y, int S)
{
if(x > INF || !t.s) return;
vis[x][y] = ; dfs(t.x, t.y, t.s);
if(t.x == x || t.y == y) dfs(x, y, S ^ t.s);
}
#undef t void Solve()
{
for(int S = ; S < bin[K]; SPFA(S ++))
for(int i = ; i <= n; i ++)
for(int j = ; j <= m; j ++)
{
for(int k = S & (S - ); k; k = (k - ) & S)
{
int t = f[i][j][k] + f[i][j][S ^ k] - a[i][j];
if(t < f[i][j][S]) { f[i][j][S] = t; path[i][j][S] = Path(i, j, k); }
}
if(f[i][j][S] != INF) { q.push(node(i, j)); vis[i][j] = ; }
}
} void Get_ans()
{
for(int i = ; i <= n; i ++)
for(int j = ; j <= m; j ++)
if(!a[i][j])
{
dfs(i, j, bin[K] - );
printf("%d\n", f[i][j][bin[K] - ]);
return;
}
} int main()
{
init();
n = read(), m = read();
for(int i = ; i <= n; i ++)
for(int j = ; j <= m; j ++)
{
a[i][j] = read();
if(!a[i][j]) K ++;
}
for(int i = ; i <= n; i ++)
for(int j = ; j <= m; j ++)
for(int k = ; k < bin[K]; k ++)
f[i][j][k] = path[i][j][k].x = INF;
K = ;
for(int i = ; i <= n; i ++)
for(int j = ; j <= m; j ++)
if(!a[i][j]) f[i][j][bin[K]] = , K ++;
Solve();
memset(vis, , sizeof(vis));
Get_ans();
for(int i = ; i <= n; i ++, putchar('\n'))
for(int j = ; j <= m; j ++)
if(!a[i][j]) putchar('x');
else if(vis[i][j]) putchar('o');
else putchar('_');
return ;
}
【题解】WC2008游览计划的更多相关文章
- 【BZOJ2595】[Wc2008]游览计划 斯坦纳树
[BZOJ2595][Wc2008]游览计划 Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为 ...
- 【LG4294】[WC2008]游览计划
[LG4294][WC2008]游览计划 题面 洛谷 bzoj 题解 斯坦纳树板子题. 斯坦纳树的总结先留个坑. 代码 #include <iostream> #include <c ...
- BZOJ_2595_[Wc2008]游览计划_斯坦纳树
BZOJ_2595_[Wc2008]游览计划_斯坦纳树 题意: 分析: 斯坦纳树裸题,有几个需要注意的地方 给出矩阵,不用自己建图,但枚举子集转移时会算两遍,需要减去当前点的权值 方案记录比较麻烦,两 ...
- [WC2008]游览计划 解题报告
[WC2008]游览计划 斯坦纳树板子题,其实就是状压dp 令\(dp_{i,s}\)表示任意点\(i\)联通关键点集合\(s\)的最小代价 然后有转移 \[ dp_{i,S}=\min_{T\in ...
- bzoj2595 / P4294 [WC2008]游览计划
P4294 [WC2008]游览计划 斯坦纳树 斯坦纳树,是一种神奇的树.它支持在一个连通图上求包含若干个选定点的最小生成树. 前置算法:spfa+状压dp+dfs(大雾) 我们设$f[o][P]$为 ...
- 【BZOJ2595】 [Wc2008]游览计划
BZOJ2595 [Wc2008]游览计划 Solution 考虑这是一个最小费用连通性的问题,既然大家都说这是什么斯坦纳树那就是的吧... 所以我们肯定可以这样设一个dp状态: \(dp_{i,j, ...
- 【BZOJ 2595】2595: [Wc2008]游览计划 (状压DP+spfa,斯坦纳树?)
2595: [Wc2008]游览计划 Time Limit: 10 Sec Memory Limit: 256 MBSec Special JudgeSubmit: 1572 Solved: 7 ...
- BZOJ2595 Wc2008 游览计划 【斯坦纳树】【状压DP】*
BZOJ2595 Wc2008 游览计划 Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个 ...
- [bzoj2595][WC2008]游览计划/[bzoj5180][Baltic2016]Cities_斯坦纳树
游览计划 bzoj-2595 wc-2008 题目大意:题目链接.题目连接. 注释:略. 想法:裸题求斯坦纳树. 斯坦纳树有两种转移方式,设$f[s][i]$表示联通状态为$s$,以$i$为根的最小代 ...
- luogu P4294 [WC2008]游览计划
LINK:游览计划 斯坦纳树例题. 斯坦纳树是这样一类问题:带权无向图上有K个关键点 求出包含这K个点的最小生成树. 也就是说 求最小生成树 但是 并不是整张图 仅限于K个点. 可以发现我们利用克鲁斯 ...
随机推荐
- Linux之MariaDB
MariaDB数据库的起源 MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可.开发这个分支的原因之一是:甲骨文公司收购了MySQL后,有将MySQL闭源的潜 ...
- XML文档处理
1)CDATA部分用<![CDATA[和]]>来限定其界限,它们是字符数据的一种特殊形式,可用使用它们来囊括那些含有<.>,&之类字符的字符串,而不必将它们解释为标记例 ...
- python的爬虫代理设置
现在网站大部分都是反爬虫技术,最简单就是加代理,写了一个代理小程序. # -*- coding: utf-8 -*- #__author__ = "雨轩恋i" #__date__ ...
- SVM中的间隔最大化
参考链接: 1.https://blog.csdn.net/TaiJi1985/article/details/75087742 2.李航<统计学习方法>7.1节 线性可分支持向量机与硬间 ...
- C# 测量程序运行时间
using System.Diagnostics; Stopwatch watch = new Stopwatch(); watch.Start(); /* 需要测量运行时间的程序 */ watch. ...
- Kubernetes-设计理念(三)
Kubernetes设计理念与分布式系统 分析和理解Kubernetes的设计理念可以使我们更深入的了解Kubernetes系统,更好的利用它管理分布式部署的云原生应用,另一方面也可以让我们借鉴其在分 ...
- (数据科学学习手札02)Python与R在循环语句与条件语句上的异同
循环是任何一种编程语言的基本设置,是进行批量操作的基础,而条件语句是进行分支运算的基础,Python与R有着各自不同的循环语句与条件语句语法,也存在着一些相同的地方. Python 1.for循环 ' ...
- POLYGON(动态规划)
学校老师布置的一道动规的题目,要求下次上课前AC.周一一放学就回家写,调试了一会儿OK了.在这边记录一下解题的思路和过程,也作为第一篇随笔,就是随便之一写,您也就随便之一看.有问题望你指出,多多包涵. ...
- POJ1679(次小生成树)
The Unique MST Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 36692 Accepted: 13368 ...
- 接口和lambda表达式笔记
接口 接口是双方,即服务提供方和想让它们的对象对服务是可用的那些类,之间约定的一种机制. 声明一个接口 public interface IntSequence{ //不提供实现,则该方法为抽象方法, ...