题目链接

f[i][s]表示以i为根节点,当前关键点的连通状态为s(每个点是否已与i连通)时的最优解。i是枚举得到的根节点,有了根节点就容易DP了。

那么i为根节点时,其状态s的更新为 \(f[i][s]=min\{f[i][s']+f[i][\complement_{s}s']-cost[i]\},s'\in s\)(枚举子集s'后,显然只需要s'的补集。减cost[i]是因为两种状态都包含,cost[i]算重了)

如果我们想合并入当前连通块一个新的非关键点v并以v为根,那么 \(f[v][s]=min\{f[k][s]+cost[v]\},k,v相邻\)

第一个更新可以按顺序,第二个更新没有明显顺序,但是如果固定状态s,很像SPFA,可类似转移。

输出方案,可以每次转移记录转移前的点与状态s,因为可能是转移点也可能是用子集更新。最后随便找一个关键点开始DFS即可。

复杂度 \(O(n\times 3^k+cE\times 2^k)\)

c为SPFA复杂度中的常数,E为边的数量,但几乎达不到全部边的数量,甚至非常小。\(3^k\)来自于子集的转移\(\sum_{i=1}^nC_n^i\times 2^i\),用二项式展开求一下和。

  1. //2080kb 148ms
  2. #include <queue>
  3. #include <cstdio>
  4. #include <cctype>
  5. #include <cstring>
  6. #include <algorithm>
  7. #define gc() getchar()
  8. #define En(i,j) (i*m+j)//Encode
  9. #define De(w) mp(w/m,w%m)//Decode
  10. #define mp std::make_pair
  11. #define pr std::pair<int,int>
  12. const int N=102,INF=1e9,to[5]={1,0,-1,0,1};
  13. int n,m,cost[N],f[(1<<10)+1][N];//换下了顺序 注意!
  14. pr pre[(1<<10)+1][N];
  15. bool inq[N],vis[N];
  16. std::queue<int> q;
  17. inline int read()
  18. {
  19. int now=0;register char c=gc();
  20. for(;!isdigit(c);c=gc());
  21. for(;isdigit(c);now=now*10+c-'0',c=gc());
  22. return now;
  23. }
  24. void SPFA(int *f,pr *pre,int s)
  25. {
  26. while(!q.empty())
  27. {
  28. int now=q.front();
  29. q.pop(), inq[now]=0;
  30. for(int x=now/m,y=now%m,xn,yn,nxt,i=0; i<4; ++i)
  31. if((xn=x+to[i])>=0&&xn<n&&(yn=y+to[i+1])>=0&&yn<m && f[nxt=En(xn,yn)]>f[now]+cost[nxt])
  32. {
  33. f[nxt]=f[now]+cost[nxt], pre[nxt]=mp(now,s);
  34. if(!inq[nxt]) inq[nxt]=1, q.push(nxt);
  35. }
  36. }
  37. }
  38. void DFS(int p,int s)
  39. {
  40. if(!pre[s][p].second) return;//pre.second即 无转移了
  41. vis[p]=1;
  42. if(pre[s][p].first==p) DFS(p,s^pre[s][p].second);
  43. DFS(pre[s][p].first,pre[s][p].second);
  44. }
  45. int main()
  46. {
  47. n=read(), m=read(); int K=0, rt=0;
  48. memset(f,0x3f,sizeof f);
  49. for(int tot=0,i=0; i<n; ++i)
  50. for(int j=0; j<m; ++j,++tot)
  51. {
  52. cost[tot]=read();
  53. if(!cost[tot]) f[1<<(K++)][tot]=0, rt=tot;
  54. }
  55. for(int s=1; s<(1<<K); ++s)
  56. {
  57. for(int i=0; i<n*m; ++i)
  58. {
  59. for(int sub=(s-1)&s; sub; sub=(sub-1)&s)
  60. if(f[s][i]>f[sub][i]+f[sub^s][i]-cost[i])
  61. f[s][i]=f[sub][i]+f[sub^s][i]-cost[i], pre[s][i]=mp(i,sub);
  62. if(f[s][i]<INF) q.push(i), inq[i]=1;//多起点,inq[]还是不能省啊
  63. }
  64. SPFA(f[s],pre[s],s);
  65. }
  66. printf("%d\n",f[(1<<K)-1][rt]);
  67. DFS(rt,(1<<K)-1);
  68. for(int i=0,tot=0; i<n; ++i,putchar('\n'))
  69. for(int j=0; j<m; ++j,++tot)
  70. if(!cost[tot]) putchar('x');
  71. else putchar(vis[tot]?'o':'_');
  72. return 0;
  73. }

BZOJ.2595.[WC2008]游览计划(DP 斯坦纳树)的更多相关文章

  1. bzoj 2595 [Wc2008]游览计划(斯坦纳树)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2595 [题意] 给定N*M的长方形,选最少权值和的格子使得要求的K个点连通. [科普] ...

  2. BZOJ_2595_[Wc2008]游览计划_斯坦纳树

    BZOJ_2595_[Wc2008]游览计划_斯坦纳树 题意: 分析: 斯坦纳树裸题,有几个需要注意的地方 给出矩阵,不用自己建图,但枚举子集转移时会算两遍,需要减去当前点的权值 方案记录比较麻烦,两 ...

  3. BZOJ 2595: [Wc2008]游览计划 [DP 状压 斯坦纳树 spfa]【学习笔记】

    传送门 题意:略 论文 <SPFA算法的优化及应用> http://www.cnblogs.com/lazycal/p/bzoj-2595.html 本题的核心就是求斯坦纳树: Stein ...

  4. BZOJ2595 Wc2008 游览计划 【斯坦纳树】【状压DP】*

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

  5. 【BZOJ2595_洛谷4294】[WC2008]游览计划(斯坦纳树_状压DP)

    上个月写的题qwq--突然想写篇博客 题目: 洛谷4294 分析: 斯坦纳树模板题. 简单来说,斯坦纳树问题就是给定一张有边权(或点权)的无向图,要求选若干条边使图中一些选定的点连通(可以经过其他点) ...

  6. BZOJ2595 WC2008游览计划(斯坦纳树)

    斯坦纳树板子题. 考虑状压dp,设f[i][j][S]表示当前在点(i,j)考虑转移,其所在的联通块包含的关键点集(至少)为S的答案. 转移时首先枚举子集,有f[i][j][S]=min{f[i][j ...

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

    [Luogu4294] 题解 : 斯坦纳树 \(dp[i][j]\) 表示以\(i\)号节点为根,当前状态为\(j\)(与\(i\)连通的点为\(1\)) 当根\(i\)不改变时状态转移方程是: \( ...

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

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

  9. bzoj:2595: [Wc2008]游览计划

    Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点:否则表示控制该方块至少需要的志愿者数 ...

随机推荐

  1. np.linespace使用方法

    np.linespace用法 觉得有用的话,欢迎一起讨论相互学习~Follow Me 生成指定范围内指定个数的一维数组 def linspace(start, stop, num=50, endpoi ...

  2. vs2015的visual assist X options安装过程

    1.安装VA_X之前,请先关闭VS2015程序. 2.下载VA_X_Setup2048.exe和VAX2048破解补丁.zip. 3.双击运行VA_X_Setup2048.exe文件,安装完成后出现 ...

  3. SQL记录-PLSQL运算符

    PL/SQL运算符 运算符是一个符号,告诉编译器执行特定的数学或逻辑操作. PL/SQL语言有丰富的内置运算符,运算符提供的以下几种类型: 算术运算符 关系运算符 比较运算符 逻辑运算符 字符串运算符 ...

  4. SpringBoot入门Demo(Hello Word Boot)

    Spring Boot 是由Pivotal团队提供的全新框架,其设计目的是用来简化新的Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置. ...

  5. Kafka 温故(一):Kafka背景及架构介绍

    一.Kafka简介 Kafka是分布式发布-订阅消息系统.它最初由LinkedIn公司开发,使用Scala语言编写,之后成为Apache项目的一部分.Kafka是一个分布式的,可划分的,多订阅者,冗余 ...

  6. Git常见错误处理

      如果输入$ Git remote add origin git@github.com:djqiang(github帐号名)/gitdemo(项目名).git  提示出错信息:fatal: remo ...

  7. python selenium - web自动化环境搭建

    前提: 安装python环境. 参考另一篇博文:https://www.cnblogs.com/Simple-Small/p/9179061.html web自动化:实现代码驱动浏览器进行点点点的操作 ...

  8. nested exception is com.svorx.core.dao.PersistenceException

    在quartz定时执行任务的时候,hibernate报错,在只读事务中进行了update语句: [ERROR] 2018/08/03 10:35:00,827 org.quartz.core.JobR ...

  9. casperjs get开头的几个dom操作使用

    getCurrentUrl() Signature: getCurrentUrl() Retrieves current page URL. Note that the url will be url ...

  10. Linux6.5 安装Python3.X(转载)

    1.获取Python 3.6.3 通过官网https://www.python.org/downloads/下载Python 3.4.3源码: 源码获取命令如下:wget https://www.py ...