~~~题面~~~

题解:

这是一道斯坦纳树的题,用状压+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. UPA深度性能报告解读

    WeTest 导读 UPA作为腾讯WeTest与Unity官方联合打造的客户端性能分析工具,为开发者提供了极大的便利和效能提升.产出的分析报告内容详尽,但您是否真的读懂了报告?是否了解每项数据的含义? ...

  2. 征战 OSG-序及目录

    其实很早就应该写这个了,一直拖到现在就是因为懒啊. 自从七月演习回来,被划到三维平台开发部,就一直混日子,也没人带领,也没人问结果,就这么一直堕落下来了,直到有一天才发现自己也看不上自己了,觉得自己这 ...

  3. Linux中常用Shell命令

    本随笔文章,由个人博客(鸟不拉屎)转移至博客园 写于:2018 年 05 月 04 日 原地址:https://niaobulashi.com/archives/linux-shell.html -- ...

  4. Python|一文简单看懂 深度&广度 优先算法

    一.前言 以后尽量每天更新一篇,也是自己的一个学习打卡!加油!今天给大家分享的是,Python里深度/广度优先算法介绍及实现. 二.深度.广度优先算法简介 1. 深度优先搜索(DepthFirstSe ...

  5. git 从头开始

    下载安装git 打开git,输入以下命令,引号内的为你自己的名字和邮箱 git config --global user.name "Your Name"git config -- ...

  6. CSP201312-2:ISBN号码

    引言:CSP(http://www.cspro.org/lead/application/ccf/login.jsp)是由中国计算机学会(CCF)发起的"计算机职业资格认证"考试, ...

  7. kafka stream 低级别的Processor API动态生成拓扑图

    public class KafkaSream { public static void main(String[] args) { Map<String, Object> props = ...

  8. linux服务器操作小技巧

    python程序后台一直运行,并将打印信息输出到文件中 nohup -u test.py > out.txt & -u 表示无缓冲,直接将打印信息输出带文件中 &表示程序后台运行

  9. isX字符串方法

    islower():返回True,如果字符串至少有一个字母,并且所有字母都是小写: 例如:>>> spam='Hello world' >>> spam.islow ...

  10. day21 TFRecord格式转换MNIST并显示

    首先简要介绍了下TFRecord格式以及内部实现protobuf协议,然后基于TFRecord格式,对MNIST数据集转换成TFRecord格式,写入本地磁盘文件,再从磁盘文件读取,通过pyplot模 ...