hdu 1879 继续畅通工程

来源:互联网 发布:电脑安装软件失败 编辑:程序博客网 时间:2024/06/06 06:52

继续畅通工程

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 13934    Accepted Submission(s): 6064


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

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

当N为0时输入结束。
 

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

Sample Input
31 2 1 01 3 2 02 3 4 031 2 1 01 3 2 02 3 4 131 2 1 01 3 2 12 3 4 10
题目大意:计算实现城通工程的,修路的最低成本难点:有的路已经修好,不能计算在内,且不能有回路关键点:开始赋值的预处理,把已经修好的路的根节点合并在一起解题时间:2014,08,19解题思路:最小生成树的Kruskal算法,注意已经修好的路体会:一开始也不知道怎么处理已经修好的路,参考别人的代码才明白最小生成树还得练****************************/ 
#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;struct xinxi{int m;int n;int cb;int zt;}a[5100];bool cmp(struct xinxi a,struct xinxi b){//对成本进行生序排列 return a.cb<b.cb;}int x[110];int find(int a){//寻找根节点 if(x[a]!=a)x[a]=find(x[a]); return x[a];}int main(){int n,i,k,g,t,sum,num;while(scanf("%d",&n),n){for(i=0;i<110;i++)x[i]=i;t=n*(n-1)/2;for(i=0;i<t;i++){scanf("%d%d%d%d",&a[i].m,&a[i].n,&a[i].cb,&a[i].zt);if(a[i].zt==1){//如果路已经修好,那么把这条路的两端的村子合并到一个集合 k=find(a[i].m);//试根节点一样,以避免下边计算重复 g=find(a[i].n);if(k!=g) x[g]=k;}}sort(a,a+t,cmp);//sort排序 sum=num=0;for(i=0;i<t&&num<n-1;i++){k=find(a[i].m);g=find(a[i].n);if(k!=g){x[g]=k;sum+=a[i].cb;num++;}}printf("%d\n",sum);}return 0;}
////
克鲁斯卡尔算法(Kruskal's algorithm)是两个经典的最小生成树算法的较为简单理解的一个。这里面充分体现了贪心算法的精髓。大致的流程可以用一个图来表示。这里的图的选择借用了Wikipedia上的那个。非常清晰且直观。
首先第一步,我们有一张图,有若干点和边
如下图所示:
.
第一步我们要做的事情就是将所有的边的长度排序,用排序的结果作为我们选择边的依据。这里再次体现了贪心算法的思想。资源排序,对局部最优的资源进行选择。
排序完成后,我们率先选择了边AD。这样我们的图就变成了
.
.
.
.
.
.
第二步,在剩下的边中寻找。我们找到了CE。这里边的权重也是5
.
.
.
.
.
.
依次类推我们找到了6,7,7。完成之后,图变成了这个样子。
.
.
.
.
.
.
下一步就是关键了。下面选择那条边呢? BC或者EF吗?都不是,尽管现在长度为8的边是最小的未选择的边。但是他们已经连通了(对于BC可以通过CE,EB来连接,类似的EF可以通过EB,BA,AD,DF来接连)。所以我们不需要选择他们。类似的BD也已经连通了(这里上图的连通线用红色表示了)。
最后就剩下EG和FG了。当然我们选择了EG。最后成功的图就是下图:
.
.
.
.
.
.
到这里所有的边点都已经连通了,一个最小生成树构建完成。
Kruskal算法的时间复杂度由排序算法决定,若采用快排则时间复杂度为O(N log N)。

3代码实现编辑

伪代码

MST-KRUSKAL(Gw)

C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include"stdio.h"
#include"stdlib.h"
struct edge
{
    int m;
    int n;
    int d;
}a[5010];
int cmp(const void *a,const void *b)//按升序排列
{
    return((struct edge *)a)->d>((struct edge *)b)->d;
}
int main(void)
{
    int i,n,t,num,min,k,g,x[100];
    printf("请输入顶点的个数:");
    scanf("%d",&n);
    t=n*(n-1)/2;
    for(i=1;i<=n;i++)
        x[i]=i;
    printf("请输入每条边的起始端点、权值:/n");
    for(i=0;i<t;i++)
        scanf("%d%d%d",&a[i].m,&a[i].n,&a[i].d);//输入每条边的权值
    qsort(a,t,sizeof(a[0]),cmp);
    min=num=0;
    for(i=0;i<t&&num<n-1;i++)
    {
        for(k=a[i].m;x[k]!=k;k=x[k])//判断线段的起始点所在的集合
            x[k]=x[x[k]];
        for(g=a[i].n;x[g]!=g;g=x[g])//判断线段的终点所在的集合
            x[g]=x[x[g]];
        if(k!=g)//如果线段的两个端点所在的集合不一样
        {
            x[g]=k;
            min+=a[i].d;
            num++;
            printf("最小生成树中加入边:%d%d/n",a[i].m,a[i].n);
        }
    }
    printf("最小生成树的权值为:%d/n",min);
    system("pause");
    return 0;
}

0 0
原创粉丝点击