写的第一道斯坦纳树的题目。斯坦纳树在信息学中的应用一般为:在\(n\)个点之间给定\(k\)条边并相应的边权,求在保证给定\(m\)个点联通的条件下的最小边权和。解决此类问题的方法即为SPFA + 状压DP。参考论文:姜碧野 《SPFA的优化与应用》

  我们用\(dp[u][S]\)表示以u为根,联通状态为S(01状态)的最小权值。以u为根:最优解必然构成一棵最小生成树。那么有两种转移方式:

  \(dp[u][S] = dp[u][k'] + dp[u][k''] - a[u] \left ( S = k' + k'' \right )\)

  这一个可以理解为\(u\)点为树上的一个分叉点,由该点分别走向两侧,一侧联通\(k'\)集合,一侧联通\(k''\)集合,通过\(u\)点联通成\(S\)集合。这种转移比较好处理,只需枚举子集即可。

  \(dp[u][S] = dp[v][S] + w[u][v]\)

  这里是从\(u\)点走向了\(v\)点,联通了这两个集合,通过\(u -> v\)这条边来连接。这一种情况就比较复杂了:\(u\)可以转移到\(v\),\(v\)也可以转移到\(u\),但这个方程式却给了我们一点联想:好像很像是最短路中的松弛操作呀。其满足三角形不等式,虽然图中有环的存在,但最优解并不构成环。所以我们用SPFA来进行这一部分的DP。

  在本题中,\(f[i][j][S]\)代表以点\(i, j\)为根,联通状态为\(S\)的最优权值。

#include <bits/stdc++.h>
using namespace std;
#define maxn 12
#define maxm (1 << 10) + 2
#define INF 1e9
int n, m, K, f[maxn][maxn][maxm];
int a[maxn][maxn], bin[maxn];
int dx[] = {, , , -}, dy[] = {, -, , };
bool vis[maxn][maxn]; struct node
{
int x, y;
node (int _x = , int _y = ) { x = _x, y = _y; }
};
queue <node> q; struct Path
{
int x, y, s;
Path (int _x = , int _y = , int _s = ) { x = _x, y = _y, s = _s; }
}path[maxn][maxn][maxm]; int read()
{
int x = , k = ;
char c;
c = getchar();
while(c < '' || c > '') { if(c == '-') k = -; c = getchar(); }
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x * k;
} void init()
{
bin[] = ;
for(int i = ; i < ; i ++) bin[i] = bin[i - ] << ;
} void SPFA(int S)
{
while(!q.empty())
{
int x = q.front().x, y = q.front().y;
vis[x][y] = , q.pop();
for(int k = ; k < ; k ++)
{
int X = x + dx[k], Y = y + dy[k];
if(X < || Y < || X > n || Y > m) continue;
if(f[X][Y][S] > f[x][y][S] + a[X][Y])
{
f[X][Y][S] = f[x][y][S] + a[X][Y];
path[X][Y][S] = Path(x, y, S);
if(!vis[X][Y]) q.push(node(X, Y)), vis[X][Y] = ;
}
}
}
} #define t path[x][y][S]
void dfs(int x, int y, int S)
{
if(x > INF || !t.s) return;
vis[x][y] = ; dfs(t.x, t.y, t.s);
if(t.x == x || t.y == y) dfs(x, y, S ^ t.s);
}
#undef t void Solve()
{
for(int S = ; S < bin[K]; SPFA(S ++))
for(int i = ; i <= n; i ++)
for(int j = ; j <= m; j ++)
{
for(int k = S & (S - ); k; k = (k - ) & S)
{
int t = f[i][j][k] + f[i][j][S ^ k] - a[i][j];
if(t < f[i][j][S]) { f[i][j][S] = t; path[i][j][S] = Path(i, j, k); }
}
if(f[i][j][S] != INF) { q.push(node(i, j)); vis[i][j] = ; }
}
} void Get_ans()
{
for(int i = ; i <= n; i ++)
for(int j = ; j <= m; j ++)
if(!a[i][j])
{
dfs(i, j, bin[K] - );
printf("%d\n", f[i][j][bin[K] - ]);
return;
}
} int main()
{
init();
n = read(), m = read();
for(int i = ; i <= n; i ++)
for(int j = ; j <= m; j ++)
{
a[i][j] = read();
if(!a[i][j]) K ++;
}
for(int i = ; i <= n; i ++)
for(int j = ; j <= m; j ++)
for(int k = ; k < bin[K]; k ++)
f[i][j][k] = path[i][j][k].x = INF;
K = ;
for(int i = ; i <= n; i ++)
for(int j = ; j <= m; j ++)
if(!a[i][j]) f[i][j][bin[K]] = , K ++;
Solve();
memset(vis, , sizeof(vis));
Get_ans();
for(int i = ; i <= n; i ++, putchar('\n'))
for(int j = ; j <= m; j ++)
if(!a[i][j]) putchar('x');
else if(vis[i][j]) putchar('o');
else putchar('_');
return ;
}

【题解】WC2008游览计划的更多相关文章

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

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

  2. 【LG4294】[WC2008]游览计划

    [LG4294][WC2008]游览计划 题面 洛谷 bzoj 题解 斯坦纳树板子题. 斯坦纳树的总结先留个坑. 代码 #include <iostream> #include <c ...

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

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

  4. [WC2008]游览计划 解题报告

    [WC2008]游览计划 斯坦纳树板子题,其实就是状压dp 令\(dp_{i,s}\)表示任意点\(i\)联通关键点集合\(s\)的最小代价 然后有转移 \[ dp_{i,S}=\min_{T\in ...

  5. bzoj2595 / P4294 [WC2008]游览计划

    P4294 [WC2008]游览计划 斯坦纳树 斯坦纳树,是一种神奇的树.它支持在一个连通图上求包含若干个选定点的最小生成树. 前置算法:spfa+状压dp+dfs(大雾) 我们设$f[o][P]$为 ...

  6. 【BZOJ2595】 [Wc2008]游览计划

    BZOJ2595 [Wc2008]游览计划 Solution 考虑这是一个最小费用连通性的问题,既然大家都说这是什么斯坦纳树那就是的吧... 所以我们肯定可以这样设一个dp状态: \(dp_{i,j, ...

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

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

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

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

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

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

  10. luogu P4294 [WC2008]游览计划

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

随机推荐

  1. springmvc请求数据的流程。

    验证了我说的,从model层中拿来的数据,不管什么类型,都是通过隐含模型,中转,放入request中的.除非你特意把这些数据放到session域中 流程含义解释:(来自网友)(1)HTTP请求到达we ...

  2. 【实现高可效的代理模式-Squid】

    普通正向代理 首先安装squid代理软件包: 端口控制 在squid server端作端口访问控制,把默认的3128端口改为1000端口 同时把squid服务代理端口添加到selinux安全子系统的允 ...

  3. node.js中http通讯模块

    创建一个服务器 首先建立一个js文件,命名为app.js写入内容: const http=require('http'); http.createServer((request,response)=& ...

  4. python 复习函数 装饰器

    # 函数 —— 2天 # 函数的定义和调用 # def 函数名(形参): #函数体 #return 返回值 #调用 函数名(实参) # 站在形参的角度上 : 位置参数,*args,默认参数(陷阱),* ...

  5. C语言实例解析精粹学习笔记——42(插入排序)

    实例说明: 将一个整数数组按从小到大的顺序进行排序.(主要学习基本的插入排序和改进的冒泡排序的算法和应用) 思路1: 从第一个数据开始,分别比较其后的数据,若比它小,则将这两个数的位置交换:从第一个数 ...

  6. python2.7练习小例子(二十九)

        29):1.题目:按相反的顺序输出列表的值. #!/usr/bin/python # -*- coding: UTF-8 -*- a = ['one', 'two', 'three'] for ...

  7. LeetCode:15. 3Sum(Medium)

    1. 原题链接 https://leetcode.com/problems/3sum/description/ 2. 题目要求 数组S = nums[n]包含n个整数,请问S中是否存在a,b,c三个整 ...

  8. Python3: 对两个字符串进行匹配

    Python里一共有三种字符串匹配方式,用于判断一个字符串是否包含另一个字符串.比如判断字符串“HelloWorld”中是否包含“World”: def stringCompare(str1, str ...

  9. 从浏览器或者Webview 中唤醒APP

    本文来自网易云社区 作者:刘新奇 移动互联时代,很多互联网服务都会同时具备网站以及移动客户端,很多人认为APP的能帮助建立更稳固的用户关系,于是经常会接到各种从浏览器.webview中唤醒APP的需求 ...

  10. javascript数据相关处理,序列化反序列化,数据编码与解码

    对象序列化简而言之,将对象转为字符串.在数据的传输过程中,经常会使用到对象序列化. javascript中常用的对象序列化:JSON.stringify(); javascript中常用的对象反序列化 ...