码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2020-05-16 19:14:40      阅读:63      评论:0      收藏:0      [点我收藏+]

标签:main   包含   一个   格式   ring   second   memset   pair   type   

题面

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

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

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

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

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

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

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

输入格式

第一行包含整数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;
}

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

标签:main   包含   一个   格式   ring   second   memset   pair   type   

原文地址:https://www.cnblogs.com/2aptx4869/p/12900955.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!