题目链接

题目大意:有一张无向图,每条边有一定的花费,给出一些点集,让你从中选出一些边,用最小的花费将每个点集内的点相互连通,可以使用点集之外的点(如果需要的话)。

算是斯坦纳树的入门题吧。

什么是斯坦纳树呢?

假定有这样的题目:给你一张无向图和一个点集,每条边有一定的花费,让你选出一些边使点集内的所有点连通,求最小花费。

可以发现,如果点集大小为2,那么就转化成了一个两点间最短路问题。

而如果点集大小为总点数,那么就转化成了一个最小生成树问题。

进一步可以推出,如果点集大小为3,那么答案就相当于选出一个点,使该点到点集内三点的距离之和最短。

那么如果点集大小为4,5,...,乃至更多,怎么办?

随着点集的增加,情况也会越来越复杂,好像一时难以找到突破点。

其实这类题有着通用的解法,也就是斯坦纳树的dp。

设$dp[S][i]$为构造出以i为根(选根只是为了方便转移,无实际意义,相当于无根树转有根树),连通的点集为S的树的最小化费,则初始状态为$dp[1<<i][i]=0$,且存在两类转移:

$dp[S][i]=\left\{\begin{matrix}\begin{aligned}&min\{dp[S'][i]+dp[S\oplus S'][i]\}\\&min\{dp[S][j]+d[j][i]\}\end{aligned}\end{matrix}\right.$

其中$S'$是$S$的子集,$d[j][i]$表示点j与点i直接相连的边的花费。

其中第一类转移是有序的,可以直接dp求解;而第二类是无序的,需要用SPFA求解(用Dijkstra理论上也可以,但通常SPFA更快)

这样的转移还存在一个问题,就是有些边可能会重复经过,但总会存在一个不会重复的最优解,所以可以忽略掉这个问题。

这是只有一个点集时的情况,如果有多个点集的话只需要再加一个转移就好了:

$mi[S]=min\{mi[S']+mi[S\oplus S']\}$

其中$mi[S]$表示将点集S连通的最小花费,初始值为$mi[S]=min\{dp[S][i]\}$,且$S$和$S\oplus S'$都要满足:对于任意一个点集中的点,要么不包含,要么全部包含。

复杂度$O(n(3^k+Am2^k))$,A为SPFA的常数,一般较小。

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=+,inf=0x3f3f3f3f;
int n,m,k,hd[N],ne,inq[N],dp[<<][N],mi[<<],SS[+],Y[N],X[N],ok[<<];
struct E {int v,c,nxt;} e[N*];
void addedge(int u,int v,int c) {e[ne]= {v,c,hd[u]},hd[u]=ne++;}
queue<int> q;
void upd(int u,int c,int S) {
if(dp[S][u]>c) {dp[S][u]=c; if(!inq[u])inq[u]=,q.push(u);}
}
void spfa(int S) {
while(q.size())q.pop();
memset(inq,,sizeof inq);
for(int i=; i<=n; ++i)if(dp[S][i]!=inf)q.push(i),inq[i]=;
while(q.size()) {
int u=q.front();
q.pop(),inq[u]=;
for(int i=hd[u]; ~i; i=e[i].nxt)upd(e[i].v,dp[S][u]+e[i].c,S);
}
}
int main() {
memset(hd,-,sizeof hd),ne=;
scanf("%d%d%d",&n,&m,&k);
while(m--) {
int u,v,c;
scanf("%d%d%d",&u,&v,&c);
addedge(u,v,c);
addedge(v,u,c);
}
for(int i=; i<k; ++i) {
int a,b;
scanf("%d%d",&a,&b);
Y[b]=i,X[i]=b;
SS[a]|=<<i;
}
for(int i=; i<=; ++i)if(SS[i])ok[SS[i]]=;
for(int i=; i<=n; ++i)spfa(i);
memset(dp,inf,sizeof dp);
for(int i=; i<k; ++i)dp[<<i][X[i]]=;
for(int S=; S<(<<k); ++S) {
for(int S2=(S-)&S; S2; S2=(S2-)&S)
for(int i=; i<=n; ++i)
dp[S][i]=min(dp[S][i],dp[S2][i]+dp[S^S2][i]);
spfa(S);
mi[S]=inf;
for(int i=; i<=n; ++i)mi[S]=min(mi[S],dp[S][i]);
}
for(int S=; S<(<<k); ++S)
for(int S2=(S-)&S; S2; S2=(S2-)&S)if(ok[S2]&&ok[S^S2])
mi[S]=min(mi[S],mi[S2]+mi[S^S2]),ok[S]=;
printf("%d\n",mi[(<<k)-]);
return ;
}

洛谷P3264 [JLOI2015]管道连接 (斯坦纳树)的更多相关文章

  1. BZOJ 4006 Luogu P3264 [JLOI2015]管道连接 (斯坦纳树、状压DP)

    题目链接: (bzoj)https://www.lydsy.com/JudgeOnline/problem.php?id=4006 (luogu)https://www.luogu.org/probl ...

  2. 洛谷P3264 [JLOI2015]管道连接(斯坦纳树)

    传送门 感觉对斯坦纳树还是有很多疑惑啊…… 等到时候noip没有爆零的话再回来填坑好了 //minamoto #include<iostream> #include<cstdio&g ...

  3. BZOJ4006 JLOI2015 管道连接(斯坦纳树生成森林)

    4006: [JLOI2015]管道连接 Time Limit: 30 Sec Memory Limit: 128 MB Description 小铭铭最近进入了某情报部门,该部门正在被如何建立安全的 ...

  4. BZOJ4006: [JLOI2015]管道连接(斯坦纳树,状压DP)

    Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1171  Solved: 639[Submit][Status][Discuss] Descripti ...

  5. 【bzoj4006】[JLOI2015]管道连接 斯坦纳树+状压dp

    题目描述 给出一张 $n$ 个点 $m$ 条边的无向图和 $p$ 个特殊点,每个特殊点有一个颜色.要求选出若干条边,使得颜色相同的特殊点在同一个连通块内.输出最小边权和. 输入 第一行包含三个整数 n ...

  6. 【BZOJ4774/4006】修路/[JLOI2015]管道连接 斯坦纳树

    [BZOJ4774]修路 Description 村子间的小路年久失修,为了保障村子之间的往来,法珞决定带领大家修路.对于边带权的无向图 G = (V, E),请选择一些边,使得1 <= i & ...

  7. bzoj 4006 [JLOI2015]管道连接——斯坦纳树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4006 除了模板,就是记录 ans[ s ] 表示 s 合法的最小代价.合法即保证 s 里同一 ...

  8. 洛谷 P7450 - [THUSCH2017] 巧克力(斯坦纳树+随机化)

    洛谷题面传送门 9.13 补之前 8.23 做的题,不愧是鸽子 tzc( 首先我们先来探讨一下如果 \(c_{i,j}\le k\) 怎么做,先考虑第一问.显然一个连通块符合条件当且仅当它能够包含所有 ...

  9. bzoj 4006 管道连接 —— 斯坦纳树+状压DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4006 用斯坦纳树求出所有关键点的各种连通情况的代价,把这个作为状压(压的是集合选择情况)的初 ...

随机推荐

  1. 修改umask后apache报错:because search permissions are missing on a component of the path,

    0.修改umask后apache报错:because search permissions are missing on a component of the path, 1.ls -lrth ./h ...

  2. python之函数形参、实参、可变长参数整体使用和分类

    形参与实参 '''def fn(形参们): pass fn(实参们)'''# 形参:定义函数,在括号内声明的变量名,用来结束外界传来的值# 实参:调用函数,在括号内传入的实际值,值可以为常量.变量.表 ...

  3. (补发)学pythion的第二天

    所学知识点: if语句的使用 在Python中,要构造分支结构可以使用if.elif和else关键字.所谓关键字就是有特殊含义的单词,像if和else就是专门用于构造分支结构的关键字,很显然你不能够使 ...

  4. SQLite基础-7.子句(一)

    目录 SQLite子句(一) 1. WHERE子句 2. LIKE子句 3. GLOB 子句 4. Oreder By 子句 SQLite子句(一) 1. WHERE子句 WHERE 子句后面跟着条件 ...

  5. SQL Server 数据备份存储过程

    原文:SQL Server 数据备份存储过程 今天开园,分享一下我一直在使用的数据备份存储过程,欢迎转载!!! ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ...

  6. 【求教 探讨】python tkinter的messagebox

    最近有一个要求,用python的tkinter制作一个messagebox,传入3个参数: title  text timeout.用户可以点击“确定” 关闭窗口:  或者 等待几秒(timeout) ...

  7. Django-choices字段值对应关系(性别)-MTV与MVC科普-Ajax发json格式与文件格式数据-contentType格式-Ajax搭配sweetalert实现删除确认弹窗-自定义分页器-批量插入-07

    目录 models 字段补充 choices 参数/字段(用的很多) MTV与MVC模型 科普 Ajax 发送 GET.POST 请求的几种常见方式 用 Ajax 做一个小案例 准备工作 动手用 Aj ...

  8. 题解 POJ1964/UVA1330/SP277 【City Game】

    题目链接: https://www.luogu.org/problemnew/show/UVA1330 http://poj.org/problem?id=1964 https://www.luogu ...

  9. Ubuntu 忘记系统登录密码,如何修改密码

    Ubuntu 忘记系统登录密码,如何修改密码. 1.重新启动,按ESC键进入Boot Menu,选择recovery mode(一般是第二个选项). 2.在#号提示符下用cat /etc/shadow ...

  10. O061、Boot from Volume

    参考https://www.cnblogs.com/CloudMan6/p/5679384.html   Volume 除了可以用作Instance的数据盘,也可以作为启动盘(Bootable Vol ...