H - 畅通工程(最小生成树)

H - 畅通工程
Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u
Submit
Status
use MathJax to parse formulas
Description
省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。经过调查评估,得到的统计表中列出了有可能建设公路的若干条道路的成本。现请你编写程序,计算出全省畅通需要的最低成本。

Input
测试输入包含若干测试用例。每个测试用例的第1行给出评估的道路条数 N、村庄数目M ( < 100 );随后的 N
行对应村庄间道路的成本,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间道路的成本(也是正整数)。为简单起见,村庄从1到M编号。当N为0时,全部输入结束,相应的结果不要输出。

Output
对每个测试用例,在1行里输出全省畅通需要的最低成本。若统计数据不足以保证畅通,则输出“?”。

Sample Input
3 3
1 2 1
1 3 2
2 3 4
1 3
2 3 2
0 100

Sample Output
3
?

第一种:
kruskal
转自大佬博客:勿在浮沙筑高台http://blog.csdn.net/luoshixian099/article/details/51908175

此算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。 
1. 把图中的所有边按代价从小到大排序; 
2. 把图中的n个顶点看成独立的n棵树组成的森林; 
3. 按权值从小到大选择边,所选的边连接的两个顶点
ui,vi,ui,vi
,应属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。 
4. 重复(3),直到所有顶点都在一颗树内或者有n-1条边为止。

嘻嘻嘻

代码:(并查集的应用)
因为题目要求任意两个城市之前都得联通
所以要用以下代码

#include <iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<math.h>
using namespace std;
struct node{
    int from,to,len;
}s[105];
int pre[105];
int ans;

void init(int m)
{
    for(int i=1;i<=m;i++)
        pre[i]=i;
}
int fi(int n)
{
    int r=n;
    while(r!=pre[r])
        r=pre[r];
    return r;
}
int mer(int a,int b)
{
    int a1,b1;
    a1=fi(a);
    b1=fi(b);
    if(a1!=b1)
    {
         pre[b1]=a1;
         return 1;     //两个数的祖先不同,即不属于同一棵树,所以把这两个城市之间的距离加进去
    }
    return 0;

}
bool cmp(node a,node b)
{
    return a.len<b.len;
}
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        ans=0;
        int flag=1;

        if(n==0)
            break;
        for(int i=1;i<=n;i++)
        {
             scanf("%d%d%d",&s[i].from,&s[i].to,&s[i].len);

        }

        sort(s+1,s+n+1,cmp);
        init(m);

        for(int i=1;i<=n;i++)
        {
            int sum=mer(s[i].from,s[i].to);
            if(sum==1)
            {
                 ans+=s[i].len;
                 flag++;
            }

        }
        if(flag==m)    //只有这时才能证明任意两城市联通
            printf("%d\n",ans);
        else
            printf("?\n");

    }
    return 0;
}

第二种:prim

每次找已选的集合和未选的集合之间任意两点的连线的最小值,每次加进去

啧啧啧
图片转自dalao博客:勿在浮沙筑高台http://blog.csdn.net/luoshixian099/article/details/51908175 嘻嘻嘻

代码:

#include <iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<math.h>
#define INF 0x3f3f3f3f
using namespace std;
int vis[105];
int cost[105][105];
int low[105];
int ans;
void prim(int m)
{
    for(int i=1;i<=m;i++)
    {
        low[i]=INF;   //low函数初始化
        vis[i]=0;    //0是未找的1是已找的
    }
    for(int i=1;i<=m;i++)   //i的取值取决于城市编号
        low[i]=cost[1][i];
    vis[1]=1;
    low[1]=0;
    for(int i=1;i<m;i++)
    {
        int minc=INF;
        int p=-1;
        for(int j=1;j<=m;j++)
           if(minc>low[j]&&!vis[j])   //找low数组中的最小值
           {
                minc=low[j];
                p=j;
           }

        if(minc==INF)    //两城市之间未连通
        {
            printf("?\n");
            return ;

        }
        ans+=minc;
        vis[p]=1;
        for(int j=1;j<=m;j++)
          if(!vis[j]&&low[j]>cost[p][j])   //更新low数组与未连接的点之间的最小值
                low[j]=cost[p][j];


    }
    printf("%d\n",ans);

}
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        ans=0;
        int a,b,c,sum;
        memset(cost,0x3f3f3f3f,sizeof(cost));
        if(n==0)
            break;
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(c<cost[a][b])
               cost[a][b]=cost[b][a]=c;   //找两点之间的最短路
        }
        prim(m);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41700151/article/details/80572625
今日推荐