题目链接

Luogu 4294

(我做这道题的时候BZOJ全站的SPJ都炸了 提交秒WA 幸好有洛谷)

题解

这道题是【斯坦纳树】的经典例题。斯坦纳树是这样一类问题:带边权无向图上有几个(一般约10个)点是【关键点】,要求选择一些边使这些点在同一个联通块内,同时要求所选的边的边权和最小。

怎么解决斯坦纳树问题?……其实,就是一种状压DP。

\(dp[i][j]\)表示以i号节点为根,当前状态为j(j的二进制中已经与i连通的点对应位置为1)。

这个“以i为根”是哪来的呢?其实i可以是联通块中任意一个点,没有额外限制,只是引入这个i就可以DP了。

当根i不改变时(即合并两个都包含i的联通块)状态转移方程是:

\[dp[i][j] = \min_{s \in j}\{dp[i][s] + dp[i][\complement_js] - val[i]\}
\]

(\(val[i]\)表示本题中i号点的权值,减去一个是因为\(dp[i][s]\)和\(dp[i][\complement_js]\)中都含有i号点的权值,要防止“加重了”)

当根改变时(即在原有联通块中加入一个新节点i并设置为根,要求i、k相邻):

\[dp[i][j] = \min\{dp[k][j] + val[i]\}
\]

第一个状态转移方程有顺序,可以直接DP;而第二个状态转移方程没有明显顺序,但可以按照最短路的SPFA算法“DP”(神奇!)。

代码

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <cmath>
  4. #include <algorithm>
  5. #include <iostream>
  6. #include <queue>
  7. #define space putchar(' ')
  8. #define enter putchar('\n')
  9. using namespace std;
  10. typedef long long ll;
  11. template <class T>
  12. void read(T &x){
  13. char c;
  14. bool op = 0;
  15. while(c = getchar(), c < '0' || c > '9')
  16. if(c == '-') op = 1;
  17. x = c - '0';
  18. while(c = getchar(), c >= '0' && c <= '9')
  19. x = x * 10 + c - '0';
  20. if(op) x = -x;
  21. }
  22. template <class T>
  23. void write(T x){
  24. if(x < 0) putchar('-'), x = -x;
  25. if(x >= 10) write(x / 10);
  26. putchar('0' + x % 10);
  27. }
  28. const int INF = 0x3f3f3f3f;
  29. int n, m, K, root, f[101][1111], a[101], ans[11][11];
  30. bool inq[101];
  31. typedef pair<int, int> par;
  32. typedef pair<par, int> rec;
  33. #define fi first
  34. #define se second
  35. #define mp make_pair
  36. #define num(u) (u.fi * m + u.se)
  37. rec pre[101][1111];
  38. const int dx[] = {0, 0, -1, 1};
  39. const int dy[] = {1, -1, 0, 0};
  40. queue<par> que;
  41. bool legal(par u){
  42. return u.fi >= 0 && u.se >= 0 && u.fi < n && u.se < m;
  43. }
  44. void spfa(int now){
  45. while(!que.empty()){
  46. par u = que.front();
  47. que.pop();
  48. inq[num(u)] = 0;
  49. for(int d = 0; d < 4; d++){
  50. par v = mp(u.fi + dx[d], u.se + dy[d]);
  51. int nu = num(u), nv = num(v);
  52. if(legal(v) && f[nv][now] > f[nu][now] + a[nv]){
  53. f[nv][now] = f[nu][now] + a[nv];
  54. if(!inq[nv]) inq[nv] = 1, que.push(v);
  55. pre[nv][now] = mp(u, now);
  56. }
  57. }
  58. }
  59. }
  60. void dfs(par u, int now){
  61. if(!pre[num(u)][now].se) return;
  62. ans[u.fi][u.se] = 1;
  63. int nu = num(u);
  64. if(pre[nu][now].fi == u) dfs(u, now ^ pre[nu][now].se);
  65. dfs(pre[nu][now].fi, pre[nu][now].se);
  66. }
  67. int main(){
  68. read(n), read(m);
  69. memset(f, 0x3f, sizeof(f));
  70. for(int i = 0, tot = 0; i < n; i++)
  71. for(int j = 0; j < m; j++){
  72. read(a[tot]);
  73. if(!a[tot]) f[tot][1 << (K++)] = 0, root = tot;
  74. tot++;
  75. }
  76. for(int now = 1; now < (1 << K); now++){
  77. for(int i = 0; i < n * m; i++){
  78. for(int s = now & (now - 1); s; s = now & (s - 1))
  79. if(f[i][now] > f[i][s] + f[i][now ^ s] - a[i]){
  80. f[i][now] = f[i][s] + f[i][now ^ s] - a[i];
  81. pre[i][now] = mp(mp(i / m, i % m), s);
  82. }
  83. if(f[i][now] < INF)
  84. que.push(mp(i / m, i % m)), inq[i] = 1;
  85. }
  86. spfa(now);
  87. }
  88. write(f[root][(1 << K) - 1]), enter;
  89. dfs(mp(root / m, root % m), (1 << K) - 1);
  90. for(int i = 0, tot = 0; i < n; i++){
  91. for(int j = 0; j < m; j++)
  92. if(!a[tot++]) putchar('x');
  93. else putchar(ans[i][j] ? 'o' : '_');
  94. enter;
  95. }
  96. return 0;
  97. }

Luogu 4294 [WC2008]游览计划 | 斯坦纳树的更多相关文章

  1. 洛谷4294 [WC2008]游览计划——斯坦纳树

    题目:https://www.luogu.org/problemnew/show/P4294 大概是状压.两种转移,一个是以同一个点为中心,S由自己的子集拼起来:一个是S相同.中心不同的同层转移. 注 ...

  2. 【BZOJ2595】[Wc2008]游览计划 斯坦纳树

    [BZOJ2595][Wc2008]游览计划 Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为 ...

  3. bzoj2595: [Wc2008]游览计划 斯坦纳树

    斯坦纳树是在一个图中选取某些特定点使其联通(可以选取额外的点),要求花费最小,最小生成树是斯坦纳树的一种特殊情况 我们用dp[i][j]来表示以i为根,和j状态是否和i联通,那么有 转移方程: dp[ ...

  4. BZOJ2595: [Wc2008]游览计划(斯坦纳树,状压DP)

    Time Limit: 10 Sec  Memory Limit: 256 MBSec  Special JudgeSubmit: 2030  Solved: 986[Submit][Status][ ...

  5. BZOJ 2595 [Wc2008]游览计划 ——斯坦纳树

    [题目分析] 斯坦纳树=子集DP+SPFA? 用来学习斯坦纳树的模板. 大概就是用二进制来表示树包含的点,然后用跟几点表示树的形态. 更新分为两种,一种是合并两个子集,一种是换根,换根用SPFA迭代即 ...

  6. bzoj2595 [Wc2008]游览计划——斯坦纳树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2595 今天刚学了斯坦纳树,还不太会,写一道题练习一下: 参考了博客:http://www.c ...

  7. P4294 [WC2008]游览计划 (斯坦纳树)

    题目链接 差不多是斯坦纳树裸题,不过边权化成了点权,这样在合并两棵子树时需要去掉根结点的权值,防止重复. 题目还要求输出解,只要在转移时记录下路径,然后dfs一遍就好了. #include<bi ...

  8. 【BZOJ-2595】游览计划 斯坦纳树

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

  9. luogu P4294 [WC2008]游览计划

    LINK:游览计划 斯坦纳树例题. 斯坦纳树是这样一类问题:带权无向图上有K个关键点 求出包含这K个点的最小生成树. 也就是说 求最小生成树 但是 并不是整张图 仅限于K个点. 可以发现我们利用克鲁斯 ...

随机推荐

  1. BootStrap学习(5)_多媒体对象&列表组

    一.多媒体对象 这些抽象的对象样式用于创建各种类型的组件(比如:博客评论),我们可以在组件中使用图文混排,图像可以左对齐或者右对齐.媒体对象可以用更少的代码来实现媒体对象与文字的混排. .media: ...

  2. core_cm4_simd.h文件是干嘛的?

    core_cm4_simd.h文件用于simd指令,即单指令多数据流,这个只有ARMv7架构才有,Cortex m3 m4 m7是ARMv7架构,而Cortex m0 m1是没有的. 所以,在新建Co ...

  3. 在Windows7上如何找到Cookie

    摘要 出于兴趣爱好,前一阵子做了一个网页,网页中需要用到Cookie,但是,根据书上的说明,并没有找打教材中所说的Cookie的位置,本文就主要介绍在计算机(Win7)中Cookie的存放位置,同样适 ...

  4. 从源码的角度再看 React JS 中的 setState

    在这一篇文章中,我们从源码的角度再次理解下 setState 的更新机制,供深入研究学习之用. 在上一篇手记「深入理解 React JS 中的 setState」中,我们简单地理解了 React 中 ...

  5. WEEK 7:团队项目的感想

    经过了几个星期的团队协作,我们的“爬虫”有了很大的完善,我作为团队中的主DEV,在这个过程中一边工作一边阅读,也有了不少的收获. Brooks的<没有银弹>告诉我们,在软件领域,没有什么绝 ...

  6. 毕业设计 之 五 PHP语法学习笔记

    毕业设计 之 四 PHP语法学习笔记 作者:20135216 平台:windows10 软件:XAMPP,DreamWeaver 说明:该笔记是对网站编程语言的详细学习 一.PHP基础 0. 关于环境 ...

  7. 第三个spring冲刺总结(附团队贡献分)

    基于调查需求下完成的四则运算,我们完成了主要的3大功能. 第一,普通的填空题运算,这个是传统的运算练习方式,团队都认为这个选项是必要的,好的传统要留下来,在个人经历中,填空练习是一个不错的选择. 第二 ...

  8. eclispe file查找

    今天查找一段js代码时在本页内找不到,所以需要在整个工程下寻找. 过程如下

  9. Protobuf一例

    Developer Guide  |  Protocol Buffers  |  Google Developershttps://developers.google.com/protocol-buf ...

  10. Windows10下Docker监控管理工具:Hyper-V管理器

    用Hyper-V管理器监控管理Docker,看到最新的MobyLinuxVM了. 今天启动Docker,出现内存不足的问题,调节内存配置即可.