BZOJ_5180_[Baltic2016]Cities_ 斯坦纳树

题意:

给定n个点,m条双向边的图。其中有k个点是重要的。每条边都有一定的长度。

现在要你选定一些边来构成一个图,要使得k个重要的点相互连通,求边的长度和的最小值。

分析:

斯坦纳树裸题

dis[i][j]表示关键点连通状态为i,当前在点j的最小花费

有两个转移:内部枚举子集,外部spfa转移

这道题卡spfa,那我们用dij就好啦

代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
using namespace std;
#define N 100050
#define LL long long
priority_queue <pair <LL,int> >q;
int head[N],to[N<<2],nxt[N<<2],cnt;
int n,m,k,id[10],vis[33][N];
LL dis[33][N],val[N<<2];
inline void add(int u,int v,LL w){
    to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;val[cnt]=w;
}
int main(){
    scanf("%d%d%d",&n,&k,&m);
    int i,j,mask=(1<<k)-1,p,x,y;
    LL z;
    memset(dis,0x3f,sizeof(dis));
    for(i=1;i<=k;i++) scanf("%d",&id[i]);
    for(i=1;i<=k;i++) dis[1<<i-1][id[i]]=0;
    for(i=1;i<=m;i++) { scanf("%d%d%lld",&x,&y,&z);add(x,y,z);add(y,x,z); }
    for(j=1;j<=mask;j++){
        for(i=1;i<=n;i++){
            for(p=j&(j-1);p;p=j&(p-1)){
                dis[j][i]=min(dis[j][i],dis[p][i]+dis[j-p][i]);
            }
        }
        for(i=1;i<=n;i++){
            q.push(make_pair(-dis[j][i],i));
        }
        while(!q.empty()){
            x=q.top().second;q.pop();
            if(vis[j][x])continue;
            vis[j][x]=1;
            for(i=head[x];i;i=nxt[i]){
                if(dis[j][to[i]]>dis[j][x]+val[i]){
                    dis[j][to[i]]=dis[j][x]+val[i];
                    q.push(make_pair(-dis[j][to[i]],to[i]));
                }
            }
        }
    }
    LL ans=1ll<<60;
    for(i=1;i<=n;i++)ans=min(ans,dis[mask][i]);
    printf("%lld\n",ans);
}

原文地址:https://www.cnblogs.com/suika/p/8542180.html

时间: 2024-10-14 12:19:34

BZOJ_5180_[Baltic2016]Cities_ 斯坦纳树的相关文章

【bzoj5180】[Baltic2016]Cities 斯坦纳树

题目描述 给定n个点,m条双向边的图.其中有k个点是重要的.每条边都有一定的长度. 现在要你选定一些边来构成一个图,要使得k个重要的点相互连通,求边的长度和的最小值. 输入 共m+2行 第1行:n,k,m,n个点,k个重要的点,m条边: 第2行共K个点 第3至第m+2行,每行包括3个数字,a,b,c,表示有一条从a到b长度为c的双向路径 k<=5 n<=10^5 1<=m<=2*(10^5) 输出 共1行,即最小长度和 样例输入 4 3 61 3 41 2 41 3 91 4 62

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

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

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

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

HDU 4085 斯坦纳树模板题

Dig The Wells Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 971    Accepted Submission(s): 416 Problem Description You may all know the famous story "Three monks". Recently they find som

【Foreign】修路 [斯坦纳树]

修路 Time Limit: 20 Sec  Memory Limit: 256 MB Description Input Output 仅一行一个整数表示答案. Sample Input 5 5 2 1 3 4 3 5 2 2 3 1 3 4 4 2 4 3 Sample Output 9 HINT Main idea 给定若干对点,选择若干边,询问满足每对点都连通的最小代价. Source 发现 d 非常小,所以我们显然可以使用斯坦纳树来求解. 斯坦纳树是用来解决这种问题的:给定若干关键点,

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

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

HDU 4085 斯坦纳树

题目大意: 给定无向图,让前k个点都能到达后k个点(保护地)中的一个,而且前k个点每个需要占据后k个中的一个,相互不冲突 找到实现这个条件达到的选择边的最小总权值 这里很容易看出,最后选到的边不保证整个图是联通的 我们只要计算出每一个连通的最小情况,最后跑一遍dfs就能计算出答案了 那么用dp[i][j]表示 i 点为根得到联通状态为 j 的情况需要选到的边的最小总权值 这个用斯坦纳树的思想就可以做到的 对于每一个状态,都用spfa跑一遍得到最优解 dp[i][j] = min(dp[i][j]

【BZOJ2595】【Wc2008】游览计划、斯坦纳树

题解:斯坦纳树,实现神马的在代码里面有还看得过去的注释. 代码: #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 15 #define inf 0x3f3f3f3f using namespace std; const int dx[]={0,0,1,-1}; const int dy[

斯坦纳树模板

屌炸天阿什么东西都有 丢 //斯坦纳树模板 让k个点联通的最小生成树 复杂度 n*3^k #include<iostream> #include<cstring> #include<set> #include<map> #include<cmath> #include<stack> #include<queue> #include<deque> #include<list> #include<