最小生成树问题解题报告

题目一:

HDOJ 1879

网址:http://acm.hdu.edu.cn/showproblem.php?pid=1879

问题描述

省政府畅通工程的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。现得到城镇道路统计表,表中列出了任意两城镇间修建道路的费用,以及该道路是否已经修通的状态。现请你编写程序,计算出全省畅通需要的最低成本。

 

输入

测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( 1< N < 100 );随后的 N(N-1)/2 行对应村庄间道路的成本及修建状态,每行给4个正整数,分别是两个村庄的编号(从1编号到N),此两村庄间道路的成本,以及修建状态:1表示已建,0表示未建。

当N为0时输入结束。

 

输出

每个测试用例的输出占一行,输出全省畅通需要的最低成本。

 

示例输入

3

1 2 1 0

1 3 2 0

2 3 4 0

3

1 2 1 0

1 3 2 0

2 3 4 1

3

1 2 1 0

1 3 2 1

2 3 4 1

0

 

示例输出

3 1 0

解题思路:

最小生成树问题可以用kruskal算法解。node结构体记录所有可以连通的边的两个端点和边的权重。将所有的边按照权重的大小进行从小到大排序,只要一个边的两个端点不在同一个集合内(避免成环),就将该边的权重加到sum上,当已经加上的边的个数等于n-1(n为点的个数)时最小生成树就产生了(树中边数等于点的个数减一)。(已经连通的两个点所在的边的权重设置为0)

*输入时用scanf进行输入

 

 

 

程序:

#include<iostream>

#include<algorithm>

#include<cstdio>

usingnamespace std;

 

constint MAX =10001;

int* p, row, num;  //num表示点的个数,row表示输入的行数

 

void set(intn)

{

    for(int i = 0; i <= n; i++)

       p[i] = i;

}

 

structnode

{

    int from;

    int to;

    int value;

}N[MAX];

 

int Find(intx)

{

    if(x == p[x])

       returnx;

    else

       return p[x] = Find(p[x]);  //路径压缩

}

 

//判断两个村庄是否相通,不相通返回true,否则返回false

bool Merge(intp1, intp2)

{

    p1 = Find(p1), p2 = Find(p2);

    if(p1 != p2)

    {

       p[p1] = p2;

       returntrue;

    }

    returnfalse;

}

 

//定义sort比较的规则

bool cmp(nodea, nodeb)

{

    returna.value < b.value;

}

 

//最小生成树算法

void kruskal()

{

    sort(N, N + row, cmp);

    int sum =0, count = 0;

    for(int i = 0; i < row; i++)

    {

       if(Merge(p[N[i].from],p[N[i].to]))

       {

           sum += N[i].value;

           count++;

       }

       if(count== num - 1)  //边数等于点的个数减一

           break;

    }

    cout << sum << endl;

}

 

int main()

{

    int n;

    while(scanf("%d",&n) && n)

    {

       p = newint[n+1];

       set(n);

       num = n;

       row = (n * (n-1)) / 2;

       for(int i = 0; i < row; i++)

       {

           int d;

           scanf("%d%d%d%d",&N[i].from,&N[i].to,&N[i].value,&d);

           if(d == 1)

              N[i].value = 0;  //已经连通的排在最前面且边的权重为0

       }

       kruskal();

       delete[] p;

    }

    return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值