uva-10600 次小生成树

ACM Contest and Blackout

题目:

In order to prepare the “The First National ACM School Contest”(in 20??) the major of the city decided to provide all the schools with a reliable source of power. (The major is really afraid of blackoutsJ). So, in order to do that, power station “Future” and one school (doesn’t matter which one) must be connected; in addition, some schools must be connected as well.

You may assume that a school has a reliable source of power if it’s connected directly to “Future”, or to any other school that has a reliable source of power. You are given the cost of connection between some schools. The major has decided to pick out two the cheapest connection plans – the cost of the connection is equal to the sum of the connections between the schools. Your task is to help the major – find the cost of the two cheapest connection plans.

Input
The Input starts with the number of test cases, T (1<=T<=15) on a line. Then T test cases follow. The first line of every test case contains two numbers, which are separated by a space, N (3<=N<=100) the number of schools in the city, and M the number of possible connections among them. Next M lines contain three numbers Ai, Bi, Ci , where Ci is the cost of the connection (1<=Ci<=300) between schools Ai and Bi. The schools are numbered with integers in the range 1 to N.

Output
For every test case print only one line of output. This line should contain two numbers separated by a single space - the cost of two the cheapest connection plans. Let S1 be the cheapest cost and S2 the next cheapest cost. It’s important, that S1=S2 if and only if there are two cheapest plans, otherwise S1<=S2. You can assume that it is always possible to find the costs S1 and S2..

题意:

感觉这是一个很裸的求次小生成树的题目了,意思就是给你一个联通图,让你求出最小生成树的值和次小生成树的值。

  • 首先求最小生成树用prim算法即可。
  • 然后考虑如何求次小生成树,次小生成树是最小生成树换一条边得到的,所以枚举所有的不在最小生成树上的边,找到分别包括他们求出的最小生成树中最小的情况就是次小生成树。
  • 求法是加入一条树外的边会形成环,为求次小生成树,在环上删除的一定是权值最大的边。就是树上从i到j路径上最大一条边。
  • 利用树形dp的思想,设dp[i][j]表示从点i到点j路径上的最大边的值,在prim算法时每次找到最小边时,遍历所有vis[i]==true的点,dp[i][index]=max(dp[i][pre[index]],minn),index表示每次找到的最小边连接的点,minn表示最小边长,pre[index]表示最小边除了index以外的另外一个点。(说的有点绕,就是找该点到其他点的最长边)。

代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <cmath>
#define N 100
#define inf 0x3f3f3f
using namespace std;
int maps[N+20][N+20];
int val[N+20];
bool vis[N+20];
bool tree[N+20][N+20];
int dp[N+20][N+20];
int pre[N+20];
int n,m;
int sum=0;
int ans=0;
void prim()
{
    for(int i=1;i<=n;i++)
        val[i]=maps[1][i],pre[i]=1;
    vis[1]=true;
    pre[1]=-1;
    int T=n-1;
    while(T--)
    {
        int minn=inf;
        int index=-1;
        for(int i=1;i<=n;i++)
        {
            if(vis[i]==false&&val[i]<minn)
                index=i,minn=val[i];
        }
        sum+=minn;
        vis[index]=true;
        tree[index][pre[index]]=tree[pre[index]][index]=true;
        dp[index][pre[index]]=dp[pre[index]][index]=minn;
        for(int i=1;i<=n;i++)
        {
            if(vis[i]==true&&i!=index)
                dp[i][index]=dp[index][i]=max(dp[i][pre[index]],dp[index][pre[index]]);
            if(vis[i]==false&&val[i]>maps[index][i])
                pre[i]=index,val[i]=maps[index][i];
        }
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        sum=0;
        memset(pre,0,sizeof(pre));
        memset(tree,0,sizeof(tree));
        memset(vis,0,sizeof(vis));
        memset(maps,inf,sizeof(maps));
        memset(val,0,sizeof(val));
        scanf("%d%d",&n,&m);
        memset(dp,0,sizeof(dp));
        int x,y,z;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            maps[x][y]=z;
            maps[y][x]=z;
        }
        prim();
        ans=inf;
        for(int i=1;i<=n;i++)
        {
            for(int j=i+1;j<=n;j++)
            {
                if(tree[i][j]==false&&maps[i][j]!=inf)
                {
                    ans=min(ans,sum+maps[i][j]-dp[i][j]);
                }
            }
        }
        printf("%d %d\n",sum,ans);
    }
    return 0;
}

P.S.感觉把一个很简单的东西讲复杂了,下次写时好好组织一下语言。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值