pku 1639 Picnic Planning 最小度限制生成树

http://poj.org/problem?id=1639

题意:

见黑书。

思路:

最小限制树模板题:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>

#define CL(a,num) memset((a),(num),sizeof(a))
#define iabs(x)  ((x) > 0 ? (x) : -(x))
#define Min(a,b) (a) > (b)? (b):(a)
#define Max(a,b) (a) > (b)? (a):(b)

#define ll long long
#define inf 0x7f7f7f7f
#define MOD 1073741824
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define test puts("<------------------->")
#define maxn 100007
#define M 150
#define N 35
using namespace std;
//freopen("din.txt","r",stdin);

struct node
{
    int u,w;
    node(int _u,int _w)
    {
        u = _u;
        w = _w;
    }
    friend bool operator < (node a,node b)
    {
        return a.w > b.w;
    }
};

int dis[N],pre[N];
int col[N],near[N];
int Msd[N];
int mat[N][N];

priority_queue<node>q;
map<string,int>mp;

int res,n;

int prim(int s,int id)
{
    int res = 0,i;
    while (!q.empty()) q.pop();
    q.push(node(s,0));
    dis[s] = 0;
    while (!q.empty())
    {
        node cur = q.top(); q.pop();
        int u = cur.u;
        int w = cur.w;
        if (!col[u])
        {
            col[u] = id;
            res += w;
            for (i = 1; i <= n; ++i)
            {
                if (!col[i] && mat[u][i] && dis[i] > mat[u][i])
                {
                    dis[i] = mat[u][i];
                   pre[i] = u;
                    q.push(node(i,dis[i]));
                }
            }
        }
    }
    return res;
}
void dfs(int u,int fa,int maxside)
{
    Msd[u] = maxside > mat[fa][u] ? maxside : mat[fa][u];
    for (int i = 1; i <= n; ++i)
    {
        if (mat[u][i] && i != fa && (pre[u] == i || pre[i] == u))
        dfs(i,u,Msd[u]);
    }
}

void solve(int k)
{
    int i;
    for (i = 0; i <= n; ++i)
    {
        dis[i] = inf;
        col[i] = 0;
        pre[i] = 0;
        near[i] = 0;
    }
    col[0] = 1;//标记每一个最小生成树,先将根节点计入1
    res = 0;
    int num = 0;
    for (i = 1; i <= n; ++i)
    {
        if (!col[i]) res += prim(i,++num);//分别计算最小生成树
    }
    //计算每个最小生成树里距离s的最短距离
    for (i = 1; i <= n; ++i)
    {
        int mk = col[i];
        if (mat[0][i] && (near[mk] == 0 || mat[0][i] < mat[0][near[mk]]))
        near[mk] = i;
    }
    //连接每个最小生成树到根节点s的最短距离同时更新每个节点可能连接的最大边权值
    CL(Msd,0);
    for (i = 1; i <= num; ++i)
    {
        res += mat[0][near[i]];
        mat[0][near[i]] = mat[near[i]][0] = 0;//将该点消除
        dfs(near[i],0,0);//求出改最小生成树以0为根节点的树根到每个节点之间的最大边权值
    }
    k = k - num;//此时形成num度最小生成树

    while (k--)
    {
        int tmp = 0;//找出Msd[tmp] - mat[0][tmp]的最大值
        for (i = 1; i <= n; ++i)
        {
            if (mat[0][i] && (tmp == 0 || Msd[tmp] - mat[0][tmp] < Msd[i] - mat[0][i]))
            tmp = i;
        }
        if (Msd[tmp] <= mat[0][tmp]) break;//如果不能使度限制生成树继续减小跳出
        res -= (Msd[tmp] - mat[0][tmp]);
        mat[0][tmp] = mat[tmp][0] = 0;//将改变置为0,表示不能再枚举了,已经加入最小限度生成树
        //
        int p = 0;
        for (i = tmp; pre[i] != 0; i = pre[i])
        {
            if (p == 0 || mat[p][pre[p]] < mat[i][pre[i]]) p = i;
        }
        pre[p] = 0;
        dfs(tmp,0,0);
    }
    printf("Total miles driven: %d\n",res);
}
int main()
{
    int i,m;
    string s1,s2;
    int w;
    while (~scanf("%d",&m))
    {
        mp.clear();
        CL(mat,0);
        mp["Park"] = 0; n = 0;
        //printf("%d\n",mp.count("Park"));
        for (i = 0; i < m; ++i)
        {
            cin>>s1>>s2>>w;
            if (!mp.count(s1)) mp[s1] = ++n;
            if (!mp.count(s2)) mp[s2] = ++n;
            int tx = mp[s1];
            int ty = mp[s2];
            //建图
            if (!mat[tx][ty] || mat[tx][ty] > w)
            mat[tx][ty] = mat[ty][tx] = w;
        }
        int k;
        cin>>k;
        solve(k);
    }
    return 0;
}

  

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值