[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;
}
原文地址:https://www.cnblogs.com/mollnn/p/14409574.html