野餐规划(最小生成树性质)?

Posted 2aptx4869

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了野餐规划(最小生成树性质)?相关的知识,希望对你有一定的参考价值。

题面

一群小丑演员,以其出色的柔术表演,可以无限量的钻进同一辆汽车中,而闻名世界。

现在他们想要去公园玩耍,但是他们的经费非常紧缺。

他们将乘车前往公园,为了减少花费,他们决定选择一种合理的乘车方式,可以使得他们去往公园需要的所有汽车行驶的总公里数最少。

为此,他们愿意通过很多人挤在同一辆车的方式,来减少汽车行驶的总花销。

由此,他们可以很多人驾车到某一个兄弟的家里,然后所有人都钻进一辆车里,再继续前进。

公园的停车场能停放的车的数量有限,而且因为公园有入场费,所以一旦一辆车子进入到公园内,就必须停在那里,不能再去接其他人。

现在请你想出一种方法,可以使得他们全都到达公园的情况下,所有汽车行驶的总路程最少。

输入格式

第一行包含整数n,表示人和人之间或人和公园之间的道路的总数量。

接下来n行,每行包含两个字符串A、B和一个整数L,用以描述人A和人B之前存在道路,路长为L,或者描述某人和公园之间存在道路,路长为L。

道路都是双向的,并且人数不超过20,表示人的名字的字符串长度不超过10,公园用“Park”表示。

再接下来一行,包含整数s,表示公园的最大停车数量。

你可以假设每个人的家都有一条通往公园的道路。

输出格式

输出“Total miles driven: xxx”,其中xxx表示所有汽车行驶的总路程。

输入样例:

10
Alphonzo Bernardo 32
Alphonzo Park 57
Alphonzo Eduardo 43
Bernardo Park 19
Bernardo Clemenzi 82
Clemenzi Park 65
Clemenzi Herb 90
Clemenzi Eduardo 109
Park Herb 24
Herb Eduardo 79
3

输出样例:

Total miles driven: 183

题解

最小生成树的子树还是最小生成树

先把题目中的s限制去掉, 就是求G = (V, E)的最小生成树

所以我们就是把G的最小生成树链接根(Park)的一些边去掉, 断出来一些子最小生成树

然后通过一些和这些子最小生成树相连的边(另一端必须连在Park上), 将断出来的子树重新练回Park

求得就是最小花费

思路清楚了, 就好写了

d[i][0] 表示第i棵子树链接Park的最小权值

d[i][j](j != 0) 表示第i棵子树和第j棵链接的边的最小权值

最多有20棵子树, 直接状压选择哪些子树还连在Park, 然后再选择断出来的子树连回去的最小边相加

最后取最小值就行了

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define per(i,a,b) for(int i=a;i>=b;--i)
#define fi first
#define se second
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;

const int maxn = 22;

struct rec { int x, y, z; } e[2000];

bool operator<(rec& a, rec& b) { return a.z < b.z; }

int n, s, fa[maxn], ans, v[maxn], res;
int b[maxn], cnt, d[maxn][maxn];
unordered_map<string, int> mp;

int get(int x) { return x == fa[x] ? x : fa[x] = get(fa[x]); }

void dfs(int cur, int k, int m, int p)
{
    if (cur >= res || (cnt - p + 1) < k) return;
    if (m == cnt) { res = cur; return; }

    if (k)
    {
        rep(i, p, cnt)
            if (!v[i])
            {
                v[i] = 1;
                dfs(cur + d[i][0], k - 1, m + 1, i + 1);
                v[i] = 0;
            }
    }
    else
        rep(i, 1, cnt)
        if (!v[i])
        {
            v[i] = 1;
            int mi = 2e9;
            rep(j, 1, cnt) if (v[j]) mi = min(mi, d[i][j]);
            dfs(cur + mi, 0, m + 1, 0);
            v[i] = 0;
        }
}

int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n; mp["Park"] = 0;

    rep(i, 1, n)
    {
        string a, b; int c;
        cin >> a >> b >> c;
        if (!mp.count(a)) mp[a] = mp.size();
        if (!mp.count(b)) mp[b] = mp.size();
        e[i] = { mp[a], mp[b], c };;
    } cin >> s;

    rep(i, 1, 21) fa[i] = i;
    memset(d, 0x3f, sizeof d);
    sort(e + 1, e + n + 1);

    rep(i, 1, n)
    {
        int x = get(e[i].x), y = get(e[i].y);
        if (y == 0) y = x, x = 0;
        if (x == y || (x == 0 && b[y])) continue;
        if (x == 0) { d[++cnt][0] = e[i].z; b[y] = cnt; res += e[i].z; }
        else if (b[x] && b[y])
        {
            d[b[y]][b[x]] = min(d[b[y]][b[x]], e[i].z);
            d[b[x]][b[y]] = d[b[y]][b[x]];
        }
        else if (b[x]) fa[y] = x, ans += e[i].z;
        else if (b[y]) fa[x] = y, ans += e[i].z;
        else fa[y] = x, ans += e[i].z;
    }

    if (cnt > s) { res = 1e9; dfs(0, s, 0, 1); }
    cout << "Total miles driven: " << ans + res;
    return 0;
}

以上是关于野餐规划(最小生成树性质)?的主要内容,如果未能解决你的问题,请参考以下文章

POJ2728Desert King 最优比率生成树

算法导论——最小生成树

数据结构一道题 根据最小生成树的Mst性质,可以得出 A最长边不包含于任一棵最小生成树 B最短边闭

MST性质(用于构造最小生成树)

Codeforces 1108F MST Unification(最小生成树性质)

图解:如何实现最小生成树(Prim算法与Kruskal算法)