bzoj 2595 斯坦纳树

题目大意:

选定一些格子保证景点对应的格子通过这些格子连通,保证选定的所有格子对应的权值和最小

这是相当于理解为将所有点形成的最小生成树

这里点的个数很少,所以可以对每一个点进行状态压缩

f[st][i]表示连通性至少为st,且经过i点的最小距离

方程1.f[st][i] = Min{f[s][i] + f[st - s][i]}(s为st的子集)

方程2.f[st][i] = Min{f[st][j] + w(i,j)}(i,j之间有边相连)

那么可以看出来大的状态总是跟小的状态有关,那么总是先求出小的状态集合

利用spfa求解所有状态对应的点跑最短路对其他格点进行松弛

我到现在也不知道为什么这样写效率会高

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <iostream>
  4 #include <queue>
  5 using namespace std;
  6 typedef pair<int,int> pii;
  7 #define N 11
  8 const int MAXN=1<<N;
  9 const int INF = 0x3f3f3f3f;
 10 int n , m ;
 11
 12 struct Node{
 13     int x , y , s;
 14     Node(){}
 15     Node(int x , int y , int s):x(x),y(y),s(s){}
 16 };
 17 Node pre[N][N][MAXN];//用于回溯找上一个节点
 18
 19 int w[N][N] , dp[N][N][MAXN] , dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
 20 bool vis[N][N] , flag[N][N];
 21 queue<pii> que;
 22
 23 bool ok(int x , int y){return x>=1&&x<=n&&y>=1&&y<=m;}
 24
 25 void spfa(int state)
 26 {
 27     while(!que.empty()){
 28         pii u = que.front();
 29         que.pop();
 30         int x = u.first , y = u.second;
 31         vis[x][y] = false;
 32         for(int i=0 ; i<4 ; i++){
 33             int xx = x+dir[i][0] , yy = y+dir[i][1];
 34             if(!ok(xx,yy)) continue;
 35             if(dp[xx][yy][state]>dp[x][y][state]+w[xx][yy]){
 36                 dp[xx][yy][state]=dp[x][y][state]+w[xx][yy];
 37                 pre[xx][yy][state] = Node(x , y , state);
 38                 if(!vis[xx][yy]) que.push(make_pair(xx , yy));
 39             }
 40         }
 41     }
 42 }
 43
 44 void huisu(int x , int y , int s)
 45 {
 46     flag[x][y] = true;
 47     if(pre[x][y][s].s == 0) return;
 48     huisu(pre[x][y][s].x , pre[x][y][s].y , pre[x][y][s].s);
 49     if(pre[x][y][s].x==x && pre[x][y][s].y==y) huisu(pre[x][y][s].x , pre[x][y][s].y , s-pre[x][y][s].s);
 50 }
 51
 52 void print()
 53 {
 54     for(int i=1 ; i<=n ; i++){
 55         for(int j=1 ; j<=m ; j++)
 56             if(!w[i][j]) printf("x");
 57             else if(flag[i][j]) printf("o");
 58             else printf("_");
 59         puts("");
 60     }
 61 }
 62
 63 int main()
 64 {
 65    // freopen("in.txt" , "r" , stdin);
 66     while(~scanf("%d%d" , &n , &m))
 67     {
 68         int num = 0;
 69         memset(dp , 0x3f , sizeof(dp));
 70         memset(pre , 0 , sizeof(pre));
 71         for(int i=1 ; i<=n ; i++){
 72             for(int j=1 ; j<=m ; j++){
 73                 scanf("%d" , &w[i][j]);
 74                 if(!w[i][j]){
 75                     dp[i][j][1<<num] = 0;
 76                     num++;
 77                 }
 78             }
 79         }
 80         int ALL_STATE = 1<<num;
 81         for(int k=1 ; k<ALL_STATE ; k++){
 82             for(int i=1 ; i<=n ; i++){
 83                 for(int j=1 ; j<=m ; j++){
 84                     for(int s=(k-1)&k ; s ; s=(s-1)&k){
 85                         int tmps = k-s;
 86                         if(dp[i][j][k]>dp[i][j][s]+dp[i][j][tmps]-w[i][j]){
 87                             dp[i][j][k] = dp[i][j][s]+dp[i][j][tmps]-w[i][j];
 88                             pre[i][j][k] = Node(i , j , s);
 89                         }
 90                     }
 91                     if(dp[i][j][k]<INF) que.push(make_pair(i , j)) , vis[i][j]=true;
 92                 }
 93             }
 94             spfa(k);
 95         }
 96         memset(flag , 0 , sizeof(flag));
 97         for(int i=1 ; i<=n ; i++)
 98             for(int j=1 ; j<=m ; j++){
 99                 if(!w[i][j]){
100                     cout<<dp[i][j][ALL_STATE-1]<<endl;
101                     huisu(i , j , ALL_STATE-1);
102                     print();
103                     return 0;
104                 }
105             }
106
107     }
108     return 0;
109 }
时间: 2024-08-12 20:05:59

bzoj 2595 斯坦纳树的相关文章

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

2595: [Wc2008]游览计划 Time Limit: 10 Sec  Memory Limit: 256 MBSec  Special JudgeSubmit: 1572  Solved: 739 Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点:否则表示控制该方块至少需要的志愿者数目. 相邻的整数用 (若干个) 空格隔开,行首行末也可能有多余的空格. Output 由 N

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

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

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

传送门 题意:略 论文 <SPFA算法的优化及应用> http://www.cnblogs.com/lazycal/p/bzoj-2595.html 本题的核心就是求斯坦纳树: Steiner Tree: Given an undirected graph with non-negative edge weights and a subset of vertices, usually referred to as terminals, the Steiner tree problem in g

BZOJ 2595 Wc2008 游览计划 斯坦纳树

题目大意:给定一个矩阵,有一些关键点,每个格子有权值,选择一些格子使所有关键点连通,求最小权值和 传说中的斯坦纳树- - 感觉不是很难理解的样子 枚举连通的状态,对于每个状态先对每个位置枚举子集进行合并,然后对这个状态的分层图进行SPFA 看了几分代码还是ZKY写的比较简洁- - 此外就是终于能通过操作符重载访问结构体里的三维数组了- - 我真是太丧病了233 #include <cstdio> #include <cstring> #include <iostream>

BZOJ 3205 [Apio2013]机器人 ——斯坦纳树

腊鸡题目,实在卡不过去. (改了一下午) 就是裸的斯坦纳树的题目,一方面合并子集,另一方面SPFA迭代求解. 优化了许多地方,甚至基数排序都写了. 还是T到死,不打算改了,就这样吧 #include <map> #include <cmath> #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm

BZOJ 3205 Apio2013 机器人 斯坦纳树

题目大意:给定一张地图,一些地方有障碍物,有k<=9个机器人,可以一推到底,遇到转向器会转向,两个编号相邻的机器人可以合并,求最少推多少次可以全部合并 令f[l][r][i][j]表示在点(i,j)将编号在[l,r]区间内的机器人全部合并的最小推动次数 则有动规方程组: f[l][r][i][j]=min{f[l][r][_i][_j]+1} ( (_i,_j)->(i,j) ) f[l][r][i][j]=min(f[l][temp][i][j]+f[temp+1][r][i][j]) (l

[APIO2013]机器人(斯坦纳树)

题目描述 VRI(Voltron 机器人学会)的工程师建造了 n 个机器人.任意两个兼容的机 器人站在同一个格子时可以合并为一个复合机器人. 我们把机器人用 1 至 n 编号(n ≤ 9).如果两个机器人的编号是连续的,那 么它们是兼容的,可以合并成一个复合机器人.最初这 n 个机器人各自都只有唯 一的编号.而一个由两个或以上的机器人合并构成的复合机器人拥有两个编号, 分别是构成它的所有机器人中最小和最大的编号. 例如,2 号机器人只可以与 1 号或 3 号机器人合并.若 2 号机器人与 3 号

WC 2008 观光计划(斯坦纳树)

题意 https://www.lydsy.com/JudgeOnline/problem.php?id=2595 思路 是一道比较裸的斯坦纳树呢- 题意等价于选出包含一些点的最小生成树,这就是斯坦纳树的功能. 举个例子,给定 \(n\) 个点,其中 \(k\) 个点被称作关键点,\(m\) 条带权边,求原图的一个权值最小的子图,这张子图图为包含这 \(k\) 个点的树. 我们定义 \(dp[i][j]\) 为关键点集合 \(i\) 与任意节点 \(j\) 连通的最小权的树.考虑转移这个 \(dp

FJoi2017 1月20日模拟赛 直线斯坦纳树(暴力+最小生成树+骗分+人工构造+随机乱搞)

[题目描述] 给定二维平面上n个整点,求该图的一个直线斯坦纳树,使得树的边长度总和尽量小. 直线斯坦纳树:使所有给定的点连通的树,所有边必须平行于坐标轴,允许在给定点外增加额外的中间节点. 如下图所示为两种直线斯坦纳树的生成方案,蓝色点为给定的点,红色点为中间节点. [输入格式] 第一行一个整数n,表示点数. 以下n行,每行两个整数表示点的x,y坐标. [输出格式] 第一行一个整数m,表示中间节点的个数. 必须满足m <= 10 * n 以下m行,每行2个整数表示中间节点的坐标. 以下n+m-1