zoukankan      html  css  js  c++  java
  • [BZOJ4774] 修路

    题目链接

    题意

      在一张无向图中选择一些边,使得所有点 $i ; (1 leq i leq d)$ 和点 $n-i+1$ 联通,求边权和最小值。

    数据范围

      $1 leq D leq 4$,$2D leq N leq 10^4$,$0 leq M leq 10^4$

    分析

      这是一道最小斯坦纳树的模板题

      与最小生成树不同,最小斯坦纳树只需要使给定的点集中的点连通,最小生成树是它的一种特殊情况

      通常我们用 $dp$ 求解,设 $f[i][s]$ 表示以 $i$ 为根,连通的点集为 $s$ 时生成树的最小边权,$g[s]$ 表示连通的点集为 $s$ 时生成树/森林的最小边权

      对于 $f$ 数组有两个转移方程 $$f[i][s]=min_{k subsetneq s}{f[i][k]+f[i][s-k]}$$ $$f[i][s]=min{f[j][s]+w[j][i]}$$

      其中后者常用最短路转移

      然后 $g$ 数组初始化为 $g[s]=min_{1 leq i leq n}{f[i][s]}$

      显然有状态转移方程 $g[s]=min_{k subsetneq s}{g[k]+g[s-k]}$

      其中点集 $k$ 和 $s-k$ 必须分别保证点集内对应点连通,因为在合并点集时两点集不一定相连

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    #include <set>
    using namespace std;
    #define ll long long
    #define inf 0x3f3f3f3f
    #define N 10005
    #define D 9
    
    int n, m, d, tot;
    int f[N][1 << D], g[1 << D], vis[N];
    int to[N << 1], len[N << 1], nxt[N << 1], head[N];
    queue<int> q;
    
    void add(int u, int v, int w) {
        to[++tot] = v; len[tot] = w;
        nxt[tot] = head[u]; head[u] = tot;
    }
    
    void spfa(int s) {
        while (!q.empty()) {
            int x = q.front(); q.pop(); vis[x] = 0;
            for (int i = head[x]; i; i = nxt[i])
                if (f[to[i]][s] > f[x][s] + len[i]) {
                    f[to[i]][s] = f[x][s] + len[i];
                    if (!vis[to[i]]) vis[to[i]] = 1, q.push(to[i]);
                }
        }
    }
    
    bool check(int s) {
        return (s & ((1 << d) - 1)) == (s >> d);
    }
    
    int main() {
        scanf("%d%d%d", &n, &m, &d);
        for (int i = 1; i <= m; i++) {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            add(u, v, w); add(v, u, w);
        }
        memset(f, 0x3f, sizeof f);
        memset(g, 0x3f, sizeof g);
        for (int i = 1; i <= d; i++)
            f[i][1 << i - 1] = f[n - i + 1][1 << d + i - 1] = 0;
        int all = 1 << (d << 1);
        for (int s = 0; s < all; s++) {
            for (int i = 1; i <= n; i++) {
                for (int k = s & (s - 1); k; k = (k - 1) & s)
                    f[i][s] = min(f[i][s], f[i][k] + f[i][s ^ k]);
                if (f[i][s] < inf) vis[i] = 1, q.push(i);
            }
            spfa(s);
            for (int i = 1; i <= n; i++) g[s] = min(g[s], f[i][s]);
        }
        for (int s = 0; s < all; s++)
            for (int k = s & (s - 1); k; k = (k - 1) & s)
                if (check(k) && check(s ^ k))
                    g[s] = min(g[s], g[k] + g[s ^ k]);
        printf("%d
    ", g[all - 1] != inf ? g[all - 1] : -1);
    
        return 0;
    }
    View Code
  • 相关阅读:
    [Linux]常用命令之【tar/zip/unzip/gzip/gunzip】
    [Git]解决: error: unable to create file src/main/webapp/xxxxxx/xxxx: Filename too long
    [Git]解决:error: The following untracked working tree files would be removed by checkout:
    [Linux]命令行分类
    [数据库/MYSQL]#解决缺陷#设置Unique索引时:"[Err] 1071
    [Java EE]辨析: POJO(PO / DTO / VO) | BO/DO | DAO
    [Java]遍历枚举类型为List
    【Vue】在Vue项目中调试Vue源码——修改Vue项目引入的vue文件
    【Vue】Vue源码解读之Component组件注册
    【Vue】驼峰命名法和短横线命名法的转换以及Vue源码中对驼峰式和大写式查找的支持
  • 原文地址:https://www.cnblogs.com/Pedesis/p/11377119.html
Copyright © 2011-2022 走看看