Picnic Planning (POJ - 1639,最小度限制生成树)

一.题目链接:

POJ-1639

二.题目大意:

给定一张 N 个点 M 条边的无向图

求出无向图的一颗最小生成树

满足 1 号节点的度数不超过给定的整数 S.

三.分析:

模板题...

步骤:

① 去除 1 号节点后,对每个连通块求最小生成树.

② 在每个连通块中(共 m 个)选取与 1 号点最近的点连接,得到最小 m 度生成树.

③ 枚举要拆边连接到 1 号点的节点,选取最优解.

详见代码.

四.代码实现:

#include <bits/stdc++.h>
using namespace std;

const int M = (int)20;
const int inf = 0x3f3f3f3f;

int ans;
int n, m, k;

map <string, int> mp;

int dis[M + 5][M + 5];

struct enode
{
    int u, v, w;
}Edge[(int)1e3 + 5], dp[M + 5];

int fa[M + 5];

int block_dis[M + 5];
int block_id[M + 5];

bool linked[M + 5][M + 5];

void init()
{
    mp.clear();
    mp["Park"] = 1;
    memset(dis, inf, sizeof(dis));
}

void read()
{
    scanf("%d", &m);
    string stru, strv;
    for(int i = 1, u, v, w; i <= m; ++i)
    {
        cin >> stru >> strv >> w;
        if(!mp.count(stru)) mp[stru] = mp.size();
        if(!mp.count(strv)) mp[strv] = mp.size();
        u = mp[stru], v = mp[strv];
        dis[u][v] = dis[v][u] = min(dis[u][v], w);
        Edge[i * 2 - 1].u = u;
        Edge[i * 2 - 1].v = v;
        Edge[i * 2 - 1].w = w;
        Edge[i * 2].u = v;
        Edge[i * 2].v = u;
        Edge[i * 2].w = w;
        linked[u][v] = linked[v][u] = 1;
    }
    n = mp.size();
    m *= 2;
    for(int i = 1; i <= n; ++i)
    {
        fa[i] = i;
        block_dis[i] = inf;
        for(int j = 1; j <= n; ++j)
        {
            linked[i][j] = 0;
        }
    }
    scanf("%d", &k);
}

bool cmp(enode a, enode b)
{
    return a.w < b.w;
}

int tofind(int x)
{
    if(x == fa[x])  return x;
    return fa[x] = tofind(fa[x]);
}

void Kruskal()
{
    sort(Edge + 1, Edge + m + 1, cmp);
    for(int i = 1, fau, fav; i <= m; ++i)
    {
        fau = tofind(Edge[i].u);
        fav = tofind(Edge[i].v);
        if(fau == 1 || fav == 1)    continue;
        if(fau != fav)
        {
            ans += Edge[i].w;
            fa[fau] = fav;
            linked[Edge[i].u][Edge[i].v] = linked[Edge[i].v][Edge[i].u] = 1;
        }
    }
}

void work1()
{
    for(int i = 2, fai; i <= n; ++i)
    {
        fai = tofind(i);
        if(block_dis[fai] > dis[1][i])
        {
            block_dis[fai] = dis[1][i];
            block_id[fai] = i;
        }
    }
    for(int i = 2, fai; i <= n; ++i)
    {
        fai = tofind(i);
        if(block_id[fai] == i)
        {
            --k;
            ans += block_dis[fai];
            linked[1][i] = linked[i][1] = 1;
        }
    }
}

void dfs(int u, int fa)
{
    for(int i = 2; i <= n; ++i)
    {
        if(fa != i && linked[u][i])
        {
            if(dp[u].w >= dis[u][i])
                dp[i] = dp[u];
            else
            {
                dp[i].u = u;
                dp[i].v = i;
                dp[i].w = dis[u][i];
            }
            dfs(i, u);
        }
    }
}

void work2()
{
    int id;
    enode tar;
    while((k--) > 0)
    {
        tar.w = inf;
        for(int i = 2; i <= n; ++i)
            dp[i].w = -inf;
        dfs(1, 0);
        for(int i = 2; i <= n; ++i)
        {
            if(tar.w > dis[1][i] - dp[i].w)
            {
                id = i;
                tar = dp[i];
                tar.w = dis[1][i] - dp[i].w;
            }
        }
        if(tar.w >= 0)  break;
        ans += tar.w;
        linked[1][id] = linked[id][1] = 1;
        linked[tar.u][tar.v] = linked[tar.v][tar.u] = 0;
    }
}

int main()
{
    init();
    read();
    Kruskal();
    work1();
    work2();
    printf("Total miles driven: %d\n", ans);
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值