POJ 1639 Picnic Planning(有限制的最小生成树)

来源:互联网 发布:中国数据网 编辑:程序博客网 时间:2024/05/22 05:15

题目地址
题意:矮人虽小却喜欢乘坐巨大的轿车,轿车大到可以装下无论多少矮人。某天,N(N≤20)个矮人打算到野外聚餐。为了集中到聚餐地点,矮人A要么开车到矮人B家中,留下自己的轿车在矮人B家,然后乘坐B的轿车同行;要么直接开车到聚餐地点,并将车停放在聚餐地。 虽然矮人的家很大,可以停放无数量轿车,但是聚餐地点却最多只能停放K辆轿车。现在给你一张加权无向图,它描述了N个矮人的家和聚餐地点,要你求出所有矮人开车的最短总路程。
思路:其实就是有限制的最小生成树,主要的思想是把根节点先删除,使得树变成m棵子树,分别求m棵子树的最小生成树再求和,然后再找每棵生成树中与根节点相距最近的边,连上,这就成为了m度的最小生成树,然后再在各个生成树中找到可以与根节点能连接的节点,选最小的,然后因为这样加边就会导致成环,所以删除环中最长的边,这样依次下去就能构成k度最小生成树(因为该题只是不要大于k,所以如果权重不再减小就可以停止了)。
推荐:2004年国家集训队论文

#include <iostream>#include <cstring>#include <string>#include <queue>#include <vector>#include <map>#include <set>#include <stack>#include <cmath>#include <cstdio>#include <algorithm>#define N 50#define M 90010#define LL __int64#define inf 0x3f3f3f3f#define lson l,mid,ans<<1#define rson mid+1,r,ans<<1|1#define getMid (l+r)>>1#define movel ans<<1#define mover ans<<1|1using namespace std;const LL mod = 1e9 + 7;map<string, int> ad;int mapp[N][N];int len[N], clo[N], pre[N], fst[N], max_side[N];int n, m, k;void init() {    ad.clear();    memset(mapp, inf, sizeof(mapp));    memset(len, inf, sizeof(len));//维护最小生成树的值    memset(clo, 0, sizeof(clo));//表明是哪个子树的    memset(pre, -1, sizeof(pre));//记录前驱    memset(fst, 0, sizeof(fst));//表明加上0->i这条边去与pack连接,让最小生成树与根节点相连}struct node {    int v, cap;    node() {}    node(int _v, int _cap) :v(_v), cap(_cap) {}    bool operator < (const node &a) const {        return a.cap<cap;    }};int Prim(int src, int id) {    priority_queue<node> q;    while (!q.empty())        q.pop();    len[src] = 0;    q.push(node(src, 0));    int ans = 0;    while (!q.empty()) {        node cur = q.top();        q.pop();        int u = cur.v;        if (!clo[u]) {            clo[u] = id;//标明是哪个子树的            ans += len[u];            for (int i = 1; i < n; i++) {                if (!clo[i] && len[i] > mapp[u][i]) {                    pre[i] = u;                    len[i] = mapp[u][i];                    q.push(node(i, len[i]));                }            }        }    }    return ans;}void updata(int cur,int last,int maxside) {//更新两个点的最大距离    if (mapp[cur][last] == inf) {        max_side[cur] = maxside;    }    else {        max_side[cur] = max(maxside, mapp[cur][last]);    }    for (int i = 1; i < n; i++) {        if (i != last&&mapp[cur][i] != inf && (pre[cur] == i || pre[i] == cur)) {            updata(i, cur, max_side[cur]);        }    }}void solve() {    int sum, cnt;    sum = 0;    cnt = 1;    for(int i=1;i<n;i++){//去除了根节点park        if (!clo[i]) {//如果还没有被之前的最小生成树搜到就以他为子树的根节点            sum += Prim(i, cnt++);//cnt图中的连通子图个数,即最小生成树个数        }    }    for (int i = 1; i < n; i++) {        int id = clo[i];        if (mapp[0][i] != inf && (!fst[id] || mapp[0][i] < mapp[0][fst[id]])) {            fst[id] = i;//表明加上0->i这条边去与pack连接        }    }    for (int i = 1; i < cnt; i++) {        sum += mapp[0][fst[i]];        mapp[0][fst[i]] = mapp[fst[i]][0] = inf;//因为这些边是不能改变的所以把边删除掉        updata(fst[i], 0, 0);    }    k = k - cnt + 1;//剩下可以加的度    while (k--) {        int tmp = 0;        for (int i = 1; i <= n; i++) {            if (mapp[0][i] != inf && (tmp == 0 || max_side[tmp] - mapp[0][tmp] < max_side[i] - mapp[0][i])) {                tmp = i;            }        }        if (max_side[tmp] <= mapp[0][tmp]) {//没有办法再减小了            break;        }        sum -= (max_side[tmp] - mapp[0][tmp]);        mapp[0][tmp] = mapp[tmp][0] = inf;//加上去以后就会有环了,所以删除环中的最长边        int p = 0;        for (int i = tmp; pre[i] != -1; i = pre[i]) {            if (p == 0 || mapp[p][pre[p]] < mapp[i][pre[i]]) {//删除最长的边                p = i;            }        }        pre[p] = -1;        updata(tmp, 0, 0);    }    cout << "Total miles driven: " << sum << endl;}int main() {    cin.sync_with_stdio(false);    string stra, strb;    int a, b, c;    while (cin >> m) {        init();        ad["Park"] = 0;        n = 1;        for (int i = 0; i < m; i++) {            cin >> stra >> strb >> c;            if (ad.find(stra) == ad.end()) {                ad[stra] = n;                a = n++;            }            else {                a = ad[stra];            }            if (ad.find(strb) == ad.end()) {                ad[strb] = n;                b = n++;            }            else {                b = ad[strb];            }            mapp[a][b] = min(mapp[a][b], c);            mapp[b][a] = min(mapp[b][a], c);        }        cin >> k;        solve();    }    return 0;}
原创粉丝点击