P4294 [WC2008]游览计划
斯坦纳树
给一个联通图,求 $k$ 个关键点联通的最小生成树权值
设 $f[o][i]$ 表示当前关键点选择状态为 $o$ ,以点 $i$ 为根的树的最小权值
初始 $f[1<<(i-1)][i]=val[i]$ ,$val[i]$ 表示点 $i$ 的权值
那么从小到大枚举状态 $o$
对于每一个状态枚举 $o$ 的真子集 $op$,
则 $f[o][i]=min(f[o][i],f[o-op][i]+f[op][i]-val[i])$ 注意代价要减去 $val[i]$ ,因为两个状态合并时点 $i$ 的代价会算两次
这样转移还不够,还要考虑一个树自己扩展出去
所以枚举与根 $i$ 相连的点 $v$
则 $f[o][v]=min(f[o][v],f[o][i]+val[v])$ ,这样dp的顺序不好确定,但是发现这个很像 SPFA 的式子,所以用 SPFA 来进行转移
总结一下,对于每个状态,先考虑树的合并,再考虑树的扩展
至于为什么这样做是对的呢:
感性理解一下,这样显然会考虑到所有的情况,所以是对的2333....
SPFA时以 $f[o][i]!=INF$ 为起点
因为此题要输出路径,所以维护一个 $fa[o][i]$ 存状态 $o,i$ 是从哪两个子树合并的,对于扩展的子树就特殊处理一下
骚操作:枚举一个状态的真子集 : $for(int op=(o-1)&o;op;op=(op-1)&o)$
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
inline int read()
{
int x=,f=; char ch=getchar();
while(ch<''||ch>'') { if(ch=='-') f=-; ch=getchar(); }
while(ch>=''&&ch<='') { x=(x<<)+(x<<)+(ch^); ch=getchar(); }
return x*f;
}
const int N=,M=,INF=1e9+;
int fir[M],from[M<<],to[M<<],cntt;
void add(int &a,int &b)
{
from[++cntt]=fir[a]; fir[a]=cntt;
to[cntt]=b;
}
int n,m,K,tot;
int val[M],pos[N],id[N][N];
int f[M][M];
bool inq[M],mp[N][N];
struct path {
int o1,x1,o2,x2;
}fa[M][M];
void SPFA(int p)
{
queue <int> q;
for(int i=;i<=tot;i++) if(f[p][i]<INF) q.push(i),inq[i]=;
while(!q.empty())
{
int x=q.front(); q.pop(); inq[x]=;
for(int i=fir[x];i;i=from[i])
{
int &v=to[i];
if(f[p][v]>f[p][x]+val[v])
{
f[p][v]=f[p][x]+val[v];
fa[p][v]=(path){p,x,-,v};//扩展的子树特殊处理成-1
if(!inq[v]) q.push(v),inq[v]=;
}
}
}
}
void dfs(int o,int x)
{
int o1=fa[o][x].o1,x1=fa[o][x].x1,o2=fa[o][x].o2,x2=fa[o][x].x2;
if(!(o1|x1|o2|x2)) return;
dfs(o1,x1);
if(o2==-) mp[(x2-)/m+][(x2-)%m+]=;//如果是-1则说明此点有志愿者
else dfs(o2,x2);//否则向下一个子树转移
}
int main()
{
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
memset(f,0x3f,sizeof(f));
n=read(),m=read(); tot=n*m;
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
{
id[i][j]=(i-)*m+j;//把点缩起来
val[id[i][j]]=read();
if(!val[id[i][j]]) pos[++K]=id[i][j];
if(j>) add(id[i][j-],id[i][j]),add(id[i][j],id[i][j-]);
if(i>) add(id[i-][j],id[i][j]),add(id[i][j],id[i-][j]);
}
int mx=(<<K)-;
for(int i=;i<=K;i++) f[<<i-][pos[i]]=;
SPFA();
for(int o=;o<=mx;o++)
{
for(int j=;j<=tot;j++)
for(int op=(o-)&o;op;op=(op-)&o)
if(f[o][j]>f[o^op][j]+f[op][j]-val[j])
{
f[o][j]=f[o^op][j]+f[op][j]-val[j];
fa[o][j]=(path){o-op,j,op,j};
}
SPFA(o);
}
int ans=INF,rt=;
for(int i=;i<=tot;i++) if(f[mx][i]<ans) ans=f[mx][i],rt=i;
dfs(mx,rt);
printf("%d\n",ans);
for(int i=;i<=n;i++)
{
for(int j=;j<=m;j++)
{
if(!val[id[i][j]]) printf("x");
else if(mp[i][j]) printf("o");
else printf("_");
}
printf("\n");
}
return ;
}
P4294 [WC2008]游览计划的更多相关文章
- bzoj2595 / P4294 [WC2008]游览计划
P4294 [WC2008]游览计划 斯坦纳树 斯坦纳树,是一种神奇的树.它支持在一个连通图上求包含若干个选定点的最小生成树. 前置算法:spfa+状压dp+dfs(大雾) 我们设$f[o][P]$为 ...
- luogu P4294 [WC2008]游览计划
LINK:游览计划 斯坦纳树例题. 斯坦纳树是这样一类问题:带权无向图上有K个关键点 求出包含这K个点的最小生成树. 也就是说 求最小生成树 但是 并不是整张图 仅限于K个点. 可以发现我们利用克鲁斯 ...
- 洛谷 P4294 [WC2008]游览计划
题目链接 不是很会呢,但似乎抄了题解后有点明白了 sol:状态DP显然,其实是要构建一棵最小生成树一样的东西,我自己的理解(可能不是很对哦希望多多指教)f[x][y][zt]就是到x,y这个点,状态为 ...
- P4294 [WC2008]游览计划 (斯坦纳树)
题目链接 差不多是斯坦纳树裸题,不过边权化成了点权,这样在合并两棵子树时需要去掉根结点的权值,防止重复. 题目还要求输出解,只要在转移时记录下路径,然后dfs一遍就好了. #include<bi ...
- BZOJ_2595_[Wc2008]游览计划_斯坦纳树
BZOJ_2595_[Wc2008]游览计划_斯坦纳树 题意: 分析: 斯坦纳树裸题,有几个需要注意的地方 给出矩阵,不用自己建图,但枚举子集转移时会算两遍,需要减去当前点的权值 方案记录比较麻烦,两 ...
- [WC2008]游览计划 解题报告
[WC2008]游览计划 斯坦纳树板子题,其实就是状压dp 令\(dp_{i,s}\)表示任意点\(i\)联通关键点集合\(s\)的最小代价 然后有转移 \[ dp_{i,S}=\min_{T\in ...
- 【BZOJ2595】 [Wc2008]游览计划
BZOJ2595 [Wc2008]游览计划 Solution 考虑这是一个最小费用连通性的问题,既然大家都说这是什么斯坦纳树那就是的吧... 所以我们肯定可以这样设一个dp状态: \(dp_{i,j, ...
- 【BZOJ2595】[Wc2008]游览计划 斯坦纳树
[BZOJ2595][Wc2008]游览计划 Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为 ...
- 【LG4294】[WC2008]游览计划
[LG4294][WC2008]游览计划 题面 洛谷 bzoj 题解 斯坦纳树板子题. 斯坦纳树的总结先留个坑. 代码 #include <iostream> #include <c ...
随机推荐
- 1-new对象与直接构建对象
#include <iostream> using namespace std; class A { public: A(){} A (int a){ this->a = a; } ...
- Linux中IO监控命令的使用分析
一篇不错的有关linux io监控命令的介绍和使用. 1.系统级IO监控 iostat iostat -xdm 1 # 个人习惯 %util 代表磁盘繁忙程度.100% 表示磁盘 ...
- C# 用代码返回上一页
若我们在后台.cs文件中想做到让浏览器返回上一页,我们可以在.cs代码中这样写 Page.ClientScript.RegisterStartupScript(Page.GetType(), &quo ...
- SpringMVC——文件的上传
一.加入依赖 commons-io-2.0.jar commons-fileupload-1.2.1.jar 二.接口MultipartResolver Spring MVC 为文件上传提供了直接的支 ...
- oracle数据库之分组查询
本章内容和大家分享的是数据当中的分组查询.分组查询复杂一点的是建立在多张表的查询的基础之上,(我们在上一节课的学习中已经给大家分享了多表查询的使用技巧,大家可以自行访问:多表查询1 多表查询2)而在 ...
- delphi json用法
用法:uses Superobject, Sperjsondelphi里有json单元. procedure TForm2.SuperObjectClick(Sender: TObject); var ...
- 用原生css实现高斯模糊、黑白等滤镜效果
—引导— 在CSS3中,有一个强大的属性,那就是filter属性,filter顾名思义就是“滤镜”的意思,用filter属性可以让图片无需PS处理就达到一些简单的显示效果. —定义和使用— filte ...
- Transaction And Lock--快照事务隔离级别
--================================================--准备数据GOCREATE DATABASE DB5GOUSE DB5GOCREATE TABLE ...
- C#ThreadPool学习
一.简介 提供一个线程池,该线程池可用于执行任务.发送工作项.处理异步 I/O.代表其他线程等待以及处理计时器 注意:线程池中启动的线程都是后台线程 二.主要方法 GetAvailableThread ...
- [.net 多线程]CountdownEvent
System.Threading.CountdownEvent 是一个同步基元,它在收到一定次数的信号之后,将会解除对其等待线程的锁定.CountdownEvent在初始化时有一个初始计数量,在每个工 ...