[WC2008]游览计划 状压DP,斯坦纳树
题解:
这是一道斯坦纳树的题,用状压+spfa来解决
什么是斯坦纳树?
一开始还以为是数据结构来着,其实跟最小生成树很像,大致就是最小生成树只能在各个点之间直接相连,而斯坦纳树则允许间接相连。
就是允许在图中添加新的点,再通过连接新的点来将指定点联通。这些新点可能是被限定的(给定点),比如这道题。
说到这里,你可能已经发现了,最小生成树其实就是斯坦纳树的一种特殊情况。
不过斯坦纳树其实是一个NPC问题,但是在做题的时候我们可以利用状压来解决小范围内的斯坦纳树问题
那么这道题应该如何解决呢?
其实我感觉这道题不知道斯坦纳树应该也可以做,反正是状压,不过知道斯坦纳树应该对解题有点帮助的吧。
首先我们观察到景点数其实是很小的,因此我们设f[i][j][s],表示当前点为i,j,状态为s时的最小代价,
同时记录path[i][j][s]表示转移到f[i][j][s]的状态
则有:
f[i][j][s]=min(f[i][j][k] + f[i][j][s^k] - a[i][j]);
f[i][j][s]=min(f[x][y][s] + a[i][j]);
现在来解释一下这两个式子:
1,f[i][j][s]=min(f[i][j][l] + f[i][j][s^k] - a[i][j]);
这个实质上是低层状态向高层状态的转移,是同一个点不同状态的合并,但由于是同一个点,因此直接相加的话当前点的权值会被计算两次,因此要减掉一个当前点的权值。
其中k表示s的一个子集
这里注意一个枚举子集的技巧:
for(int k=S & (S - 1); k ;k = (k - 1) & S)
其实本来应该是从k=S开始的,但是由于k=S,那么S^k将为空集,而在本题中,用空集去更新状态是毫无意义的,因此直接就跳到(S - 1) & S了
2,f[i][j][s]=min(f[x][y][s] + a[i][j]);
其实这个的意义我认为是在于向非景点的转移,转移方法是枚举与x,y相邻的点(上下左右),然后更新(式子中i,j就是被更新点)
初始化:
我们在读入数据时顺便存下景点有哪些(新开数组),这样后面处理的时候会方便很多,
然后根据我们存景点的顺序为景点编号,
首先memset(f,127,sizeof(f));
然后将每个景点对应的状态赋为0(就是自己到自己无需代价)
注意到第一个方程我们只需枚举子集即可,但是要求子集已经是最优
同时注意到第二个方程,它的转移方向是不明确的,即a可以到b,b也可以到a,所以就不方便直接枚举了,
这里我们采用spfa来维护它。
但是注意到上一个方程的要求:子集已经是最优,
因此我们需要每到一个状态,就调用一次spfa,来在当前状态下更新整张图,使得子集为最优。
具体细节看代码吧。
#include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 1050
#define inf 1061109567//直接用127太大了,会爆
#define ac 15
int n,m,tot,all,head,tail;
int s[ac][ac],f[ac][ac][AC];
int a[]={,,-,,},b[]={,,,,-};
bool z[][],vis[][]; struct node{
int x,y;
}sights[ac],q[AC]; struct Path{
int x,y,s;
}path[ac][ac][AC]; /*f[i][j][k],表示在i,j状态为k的最小代价,只能向相邻的转移 ,
为什么要按层spfa????
观察到按点转移的方程中,枚举的是子集,也就是说,当前状态的子集必须先求出,
因此要按层来spfa?*/
void pre()
{
scanf("%d%d",&n,&m);
for(R i=;i<=n;i++)
for(R j=;j<=m;j++)
{
scanf("%d",&s[i][j]);
if(!s[i][j]) sights[++tot].x=i,sights[tot].y=j;
}
all=( << tot) - ;
memset(f,,sizeof(f));
for(R i=;i<=tot;i++)
{
int x=sights[i].x,y=sights[i].y;
f[x][y][ << (i - )]=;//到自己当然没代价了
}
} void spfa(int S)//DP
{
int x,y; node now;
while(head < tail)
{
x=q[++head].x,y=q[head].y;
vis[x][y]=false;
for(R i=;i<=;i++)
{
now.x=x + a[i], now.y=y + b[i];//error!!!now.y是+ b[i]不是+a[i]啊
if(now.x < || now.x > n || now.y < || now.y > m) continue;
if(f[now.x][now.y][S] > f[x][y][S] + s[now.x][now.y])//error!!!是大于号啊。。。
{
f[now.x][now.y][S] = f[x][y][S] + s[now.x][now.y];
path[now.x][now.y][S] = (Path){x,y,S};
if(!vis[now.x][now.y]) q[++tail]=now,vis[now.x][now.y]=true;
}
}
}
head = tail = ;//重置一次防止越界
} void work()//更新
{
int now;
for(R S=;S<=all;S++)
{
for(R i=;i<=n;i++)
for(R j=;j<=m;j++)//枚举每个点
{
for(R k=S & (S - ); k ;k = (k - ) & S)//其实原版是k = S; k ;k=(k - 1) & S
{//感性的理解都是相当于从0到S枚举了每一个数,然后&S保证是子集,用k-1这样类似递归的转移来跳过某些重复部分
now=f[i][j][k] + f[i][j][S ^ k] - s[i][j];//因为都是从同一个点出发的,而一个点的代价只能算一次,所以-s[i][j]
if(now < f[i][j][S])
f[i][j][S] = now , path[i][j][S] = (Path){i,j,k};//记录路径
}
if(f[i][j][S] != inf)
q[++tail]=(node){i,j} , vis[i][j]=true;//是++tail啊
}
spfa(S);
}
} #define t path[x][y][S]
void dfs(int x,int y,int S)//搜索方案
{
if(!x || !t.s) return ;//没有上一个了就退出
z[x][y]=true;//表示有志愿者
dfs(t.x,t.y,t.s);//找当前状态的上一个
if(t.x == x && t.y == y) dfs(x,y,S ^ t.s);//如果是当前节点,将会有分叉
}
#undef t void getans()//获取答案 + 输出
{
dfs(sights[].x,sights[].y,all);
printf("%d\n",f[sights[].x][sights[].y][all]);
for(R i=;i<=n;i++)
{
for(R j=;j<=m;j++)
{
if(!s[i][j]) printf("x");
else if(z[i][j]) printf("o");
else printf("_");
}
printf("\n");
}
} int main()
{
// freopen("in.in","r",stdin);
pre();
work();
getans();
// fclose(stdin);
return ;
}
[WC2008]游览计划 状压DP,斯坦纳树的更多相关文章
- luogu4294 [WC2008]游览计划(状压DP/斯坦纳树)
link 题目大意:给定一个网格图,有些点是关键点,选择格点有代价,求把所有关键点联通的最小代价 斯坦纳树模板题 斯坦纳树问题:给定一个图结构,有一些点是关键点,求把这些关键点联通的最小代价e 斯坦纳 ...
- [WC2008]游览计划(状压dp)
题面太鬼畜不粘了. 题意就是给一张n*m的网格图,每个点有点权,有k个关键点,让你把这k个关键点连成一个联通快的最小代价. 题解 这题nmk都非常小,解法肯定是状压,比较一般的解法插头dp,但不太好写 ...
- [bzoj2595][WC2008]游览计划/[bzoj5180][Baltic2016]Cities_斯坦纳树
游览计划 bzoj-2595 wc-2008 题目大意:题目链接.题目连接. 注释:略. 想法:裸题求斯坦纳树. 斯坦纳树有两种转移方式,设$f[s][i]$表示联通状态为$s$,以$i$为根的最小代 ...
- [BZOJ4006][JLOI2015]管道连接 状压dp+斯坦纳树
4006: [JLOI2015]管道连接 Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 1020 Solved: 552[Submit][Statu ...
- 动态规划:状压DP-斯坦纳树
最小生成树是最小斯坦纳树的一种特殊情况 最小生成树是在给定的点集和边中寻求最短网络使所有点连通 而最小斯坦纳树允许在给定点外增加额外的点,使生成的最短网络开销最小 BZOJ2595 题意是给定一个棋盘 ...
- BZOJ.2595.[WC2008]游览计划(DP 斯坦纳树)
题目链接 f[i][s]表示以i为根节点,当前关键点的连通状态为s(每个点是否已与i连通)时的最优解.i是枚举得到的根节点,有了根节点就容易DP了. 那么i为根节点时,其状态s的更新为 \(f[i][ ...
- 【状压dp】Trie 树 @中山纪念中学20170304
目录 Trie 树 PROBLEM 题目描述 输入 输出 样例输入 样例输出 SOLUTION CODE Trie 树 PROBLEM 题目描述 字母(Trie)树是一个表示一个字符串集合中所有字符串 ...
- HDU.3311.Dig The Wells(DP 斯坦纳树)
题目链接 \(Description\) 有n座庙.一共n+m个点,可以在任意一些点修建水井,不同位置花费不同:也可以某些点之间连无向边共享水.求使n座庙都有水的最小花费. \(Solution\) ...
- 【BZOJ 2595】2595: [Wc2008]游览计划 (状压DP+spfa,斯坦纳树?)
2595: [Wc2008]游览计划 Time Limit: 10 Sec Memory Limit: 256 MBSec Special JudgeSubmit: 1572 Solved: 7 ...
随机推荐
- <cfloat> (float.h)
头文件: <cfloat> (float.h) 浮点类型的特性 这个头文件为特殊系统和编译器的实现描述了浮点类型的特征. 一个浮点数包含四个元素: 一个标志(a sign):正或负; 一个 ...
- Spring Boot 示例项目
Spring Boot 基于注解式开发 maven REST 示例项目 项目地址:https://github.com/windwant/spring-boot-service 项目地址: ...
- 关于html2canvas清晰度
最近有个小项目 需要生成海报让用户去分享~~~vue做的,海报通过html2canvas 生成. 遇到的最大问题是生成图片的清晰度~~网上找了好多方法. 放大倍数!~网上找的~~ var cntEle ...
- 【WXS全局对象】Date
属性: 名称 说明 Date.parse( [dateString] ) 解析一个日期时间字符串,并返回 1970/1/1 午夜距离该日期时间的毫秒数. Date.UTC(year,month,day ...
- CSP201312-2:ISBN号码
引言:CSP(http://www.cspro.org/lead/application/ccf/login.jsp)是由中国计算机学会(CCF)发起的"计算机职业资格认证"考试, ...
- JAVA基础:ArrayList和LinkedList区别
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构. 2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList ...
- ionic 日期插件学习
<ion-header> <ion-navbar> <ion-title> DateTime </ion-title> </ion-navbar& ...
- 【RL系列】Multi-Armed Bandit笔记补充(一)
在此之前,请先阅读上一篇文章:[RL系列]Multi-Armed Bandit笔记 本篇的主题就如标题所示,只是上一篇文章的补充,主要关注两道来自于Reinforcement Learning: An ...
- hadoop问题集(2)
28. Sqoop: java.lang.NullPointerException sqoop import --connect jdbc:oracle:thin:@//xxxx:1521/aps ...
- aria2 on ubuntu
http://www.5yun.org/9102.html http://jpollo.logdown.com/posts/160847-aria2c-and-yaaw aria2c --enable ...