~~~题面~~~

题解:

这是一道斯坦纳树的题,用状压+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,斯坦纳树的更多相关文章

  1. luogu4294 [WC2008]游览计划(状压DP/斯坦纳树)

    link 题目大意:给定一个网格图,有些点是关键点,选择格点有代价,求把所有关键点联通的最小代价 斯坦纳树模板题 斯坦纳树问题:给定一个图结构,有一些点是关键点,求把这些关键点联通的最小代价e 斯坦纳 ...

  2. [WC2008]游览计划(状压dp)

    题面太鬼畜不粘了. 题意就是给一张n*m的网格图,每个点有点权,有k个关键点,让你把这k个关键点连成一个联通快的最小代价. 题解 这题nmk都非常小,解法肯定是状压,比较一般的解法插头dp,但不太好写 ...

  3. [bzoj2595][WC2008]游览计划/[bzoj5180][Baltic2016]Cities_斯坦纳树

    游览计划 bzoj-2595 wc-2008 题目大意:题目链接.题目连接. 注释:略. 想法:裸题求斯坦纳树. 斯坦纳树有两种转移方式,设$f[s][i]$表示联通状态为$s$,以$i$为根的最小代 ...

  4. [BZOJ4006][JLOI2015]管道连接 状压dp+斯坦纳树

    4006: [JLOI2015]管道连接 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1020  Solved: 552[Submit][Statu ...

  5. 动态规划:状压DP-斯坦纳树

    最小生成树是最小斯坦纳树的一种特殊情况 最小生成树是在给定的点集和边中寻求最短网络使所有点连通 而最小斯坦纳树允许在给定点外增加额外的点,使生成的最短网络开销最小 BZOJ2595 题意是给定一个棋盘 ...

  6. BZOJ.2595.[WC2008]游览计划(DP 斯坦纳树)

    题目链接 f[i][s]表示以i为根节点,当前关键点的连通状态为s(每个点是否已与i连通)时的最优解.i是枚举得到的根节点,有了根节点就容易DP了. 那么i为根节点时,其状态s的更新为 \(f[i][ ...

  7. 【状压dp】Trie 树 @中山纪念中学20170304

    目录 Trie 树 PROBLEM 题目描述 输入 输出 样例输入 样例输出 SOLUTION CODE Trie 树 PROBLEM 题目描述 字母(Trie)树是一个表示一个字符串集合中所有字符串 ...

  8. HDU.3311.Dig The Wells(DP 斯坦纳树)

    题目链接 \(Description\) 有n座庙.一共n+m个点,可以在任意一些点修建水井,不同位置花费不同:也可以某些点之间连无向边共享水.求使n座庙都有水的最小花费. \(Solution\) ...

  9. 【BZOJ 2595】2595: [Wc2008]游览计划 (状压DP+spfa,斯坦纳树?)

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

随机推荐

  1. Redis系列六 Redis事务

    Redis事务 1.介绍 在Redis事务中可以一次执行多个命令,本质是一组命令的集合.一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞. 2.事务的作用 一个队列中, ...

  2. GitLab 自动触发 Jenkins 构建

    GitLab 是当前应用非常广泛的 Git Hosting 工具,Jenkins 是非常牛逼的持续集成工具.尽管 GitLab 有内建的 GitLab CI,但它远没有 Jenkins 那么强大好用. ...

  3. 「Python」matplotlib备忘录

    总结了一下网上现有的资源,得到了一些东西.随手做个备忘. 更多设置见:https://matplotlib.org/users/customizing.html. 导入 import matplotl ...

  4. uvaoj 10474 - Where is the Marble?(sort+lower_bound)

    https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...

  5. beauifulsoup模块的介绍

    01   爬虫基础知识介绍 相关库:1.requests,re  2.BeautifulSoup   3.hackhttp 使用requests发起get,post请求,获取状态码,内容: 使用re匹 ...

  6. python 定位文件目录

    经常有引用文件的地方,所以整理了一下如何定位文件目录的方法 定位当前文件的目录 import os file_path = os.path.dirname(__file__) 定位当前文件的父目录 i ...

  7. Java 递归 反射 正则表达式

    一 递归 1. 就是函数自身调用自身 (就是在栈内存中不断的加载同一个函数) 2. 什么时候用递归呢? 当一个功能被重复使用 而每一次使用该功能时的参数不确定 都由上次的功能元素结果来确定 简单说: ...

  8. 第4章 TCP/IP通信案例:访问Internet上的Web服务器

    第4章 TCP/IP通信案例:访问Internet上的Web服务器 4.2 部署代理服务器 书中为了演示访问Internet上的Web服务器的全过程,使用了squid代理服务器程序模拟了一个代理服务器 ...

  9. Redis4.0支持的新功能说明

    本文以华为云DCS for Redis版本为例,介绍Redis4.0的新功能.文章转载自华为云帮助中心. 与Redis3.x版本相比,DCS的Redis4.x以上版本,除了开源Redis增加的特性之外 ...

  10. kubernetes相关

    1.获取client , api-server 加token 或in-cluster方式 2.所有对象均有list update get 等方法 3.对象属性源码追踪,yaml与源码一一对应 4.一些 ...