zoukankan      html  css  js  c++  java
  • 【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 6
    1 3 4
    1 2 4
    1 3 9
    1 4 6
    2 3 2
    2 4 5
    3 4 8

    样例输出

    11


    题解

    斯坦纳树裸题

    斯坦纳树:给出一些点,选出若干条边使得这些点连通,求总边权的最值。

    斯坦纳树是NPC问题,不存在多项式时间内的解法,求解方法是状压dp。

    设 $f[i][j]$ 表示选择若干条边,使得状态为 $i$ 的给定点连通,并且当前可以选择下一条边的端点为 $j$ 的最小边权和。初始状态 $f[2^i][pos[i]]=0$ ,其中 $pos[i]$ 为第 $i$ 个给定点的编号。

    那么我们对于每个 $i$ 和 $j$ ,首先枚举 $i$ 的子集 $k$ ,用 $f[k][j]+f[i-k][j]$ 更新 $f[i][j]$ 。

    然后再考虑同层转移:如果 $x$ 与 $y$ 边权为 $z$ ,用 $f[i][x]+z$ 更新 $f[i][y]$ ,用 $f[i][y]$ 更新 $f[i][x]$ 。容易发现这个转移就是最短路,因此使用堆优化Dijkstra跑一遍得出所有的 $f[i][j]$ 。

    最终答案就是 $min{f[2^k-1][i]}$ 

    时间复杂度 $O(3^k·n+2^k·mlog n)$ 

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 100010
    using namespace std;
    typedef long long ll;
    typedef pair<ll , int> pr;
    priority_queue<pr> q;
    int head[N] , to[N << 2] , next[N << 2] , cnt , vis[33][N];
    ll len[N << 2] , f[33][N];
    inline void add(int x , int y , ll z)
    {
        to[++cnt] = y , len[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
    }
    int main()
    {
        int n , p , m , i , j , k , x , y;
        ll z , ans = 1ll << 62;
        scanf("%d%d%d" , &n , &p , &m);
        memset(f , 0x3f , sizeof(f));
        for(i = 0 ; i < p ; i ++ ) scanf("%d" , &x) , f[1 << i][x] = 0;
        for(i = 0 ; i < m ; i ++ ) scanf("%d%d%lld" , &x , &y , &z) , add(x , y , z) , add(y , x , z);
        for(i = 1 ; i < (1 << p) ; i ++ )
        {
            for(j = i ; j ; j = i & (j - 1))
                for(k = 1 ; k <= n ; k ++ )
                    f[i][k] = min(f[i][k] , f[j][k] + f[i ^ j][k]);
            for(j = 1 ; j <= n ; j ++ ) q.push(pr(-f[i][j] , j));
            while(!q.empty())
            {
                x = q.top().second , q.pop();
                if(vis[i][x]) continue;
                vis[i][x] = 1;
                for(j = head[x] ; j ; j = next[j])
                    if(f[i][to[j]] > f[i][x] + len[j])
                        f[i][to[j]] = f[i][x] + len[j] , q.push(pr(-f[i][to[j]] , to[j]));
            }
        }
        for(i = 1 ; i <= n ; i ++ ) ans = min(ans , f[(1 << p) - 1][i]);
        printf("%lld
    " , ans);
        return 0;
    }
    
  • 相关阅读:
    Matlab/Simulink仿真中如何将Scope转化为Figure?
    圆形Camera预览实现
    [转]自定义Drawable实现灵动的红鲤鱼动画(下篇)
    [转]自定义Drawable实现灵动的红鲤鱼动画(上篇)
    EventBus源码分析
    Camera 预览变形问题解决
    使用javacv录像,同时进行讯飞声纹认证
    Camera2必知必会
    Flutter混合工程改造实践
    讯飞语音唤醒SDK集成流程
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8485780.html
Copyright © 2011-2022 走看看