zoukankan      html  css  js  c++  java
  • [CF21D] Traveling Graph

    [CF21D] Traveling Graph - 状压dp,最短路,并查集

    Description

    给定一个有 n (n<=15) 个顶点,m(m<=2000) 条边的带边权无向图,要求从顶点 1 开始经过每一条边至少一次最后回到起点 1,要求走过的边权值总和最小。

    Solution

    转化为:至少加多少权的边能满足所有点度为偶数

    在 u,v 之间加边有直接和间接,为了把间接转化为直接,用 floyd 求 (g[][])

    考虑状压 dp,(f[s]) 表示状态 s 集合中的点的度数都为偶数了的最小花费,转移时,每次从没活的点里找两个奇数的转移一下

    注意要用并查集判断一下连通性

    #include <bits/stdc++.h>
    using namespace std;
    
    #define int long long
    
    int getb(int x, int b)
    {
        --b;
        return (x >> b) & 1;
    }
    void setb(int &x, int b)
    {
        --b;
        x |= (1 << b);
    }
    
    const int N = 20;
    int g[N][N], f[100005], n, m, d[N], fa[N], siz[N];
    
    int find(int x)
    {
        return x == fa[x] ? x : fa[x] = find(fa[x]);
    }
    
    void merge(int x, int y)
    {
        x = find(x);
        y = find(y);
        fa[x] = y;
        siz[y] += siz[x];
    }
    
    signed main()
    {
        ios::sync_with_stdio(false);
        cin >> n >> m;
        memset(g, 0x3f, sizeof g);
        for (int i = 1; i <= n; i++)
            fa[i] = i, siz[i] = 0;
        int ans = 0;
        for (int i = 1; i <= m; i++)
        {
            int u, v, w;
            cin >> u >> v >> w;
            merge(u, v);
            g[u][v] = min(g[u][v], w);
            g[v][u] = g[u][v];
            siz[u]++;
            siz[v]++;
            d[u]++;
            d[v]++;
            ans += w;
        }
    
        vector<int> vec;
        for (int i = 1; i <= n; i++)
            if (find(i) != find(1) && siz[find(i)])
            {
                cout << -1;
                return 0;
            }
    
        for (int k = 1; k <= n; k++)
        {
            for (int i = 1; i <= n; i++)
            {
                for (int j = 1; j <= n; j++)
                {
                    g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
                }
            }
        }
    
        int s0 = 0;
        for (int i = 1; i <= n; i++)
            if (d[i] % 2 == 0)
                setb(s0, i);
        memset(f, 0x3f, sizeof f);
        f[s0] = 0;
        for (int s = 0; s < 1 << n; s++)
        {
            for (int i = 1; i <= n; i++)
                if (getb(s, i) == 0)
                {
                    for (int j = 1; j < i; j++)
                        if (getb(s, j) == 0)
                        {
                            int ns = s;
                            setb(ns, i);
                            setb(ns, j);
                            f[ns] = min(f[ns], f[s] + g[i][j]);
                        }
                }
        }
        int se = (1 << n) - 1;
        ans += f[se];
        if (ans < 1e12)
            cout << ans << endl;
        else
            cout << -1 << endl;
    }
    
  • 相关阅读:
    __doPostBack的使用
    【转】function,new,constructor and prototye
    谈谈一些网页游戏失败的原因到底有哪些?(转)
    全面剖析页游巨头发家史(转)
    2013.02.20开通博客
    老子喜欢的女人
    如何成为强大的程序员?(转)
    注重健康
    学习方法总结
    数据库知识点滴积累
  • 原文地址:https://www.cnblogs.com/mollnn/p/14409574.html
Copyright © 2011-2022 走看看