畅通工程之最低成本建设问题

题目描述:
某地区经过对城镇交通状况的调查,得到现有城镇间快速道路的统计数据,并提出“畅通工程”的目标:使整个地区任何两个城镇间都可以实现快速交通(但不一定有直接的快速道路相连,只要互相间接通过快速路可达即可)。现得到城镇道路统计表,表中列出了有可能建设成快速路的若干条道路的成本,求畅通工程需要的最低成本。

输入格式:
输入的第一行给出城镇数目N (1<N≤1000)和候选道路数目M≤3N;随后的M行,每行给出3个正整数,分别是该条道路直接连通的两个城镇的编号(从1编号到N)以及该道路改建的预算成本。

输出格式:
输出畅通工程需要的最低成本。如果输入数据不足以保证畅通,则输出“Impossible”。

输入样例1:
6 15
1 2 5
1 3 3
1 4 7
1 5 4
1 6 2
2 3 4
2 4 6
2 5 2
2 6 6
3 4 6
3 5 1
3 6 1
4 5 10
4 6 8
5 6 3
输出样例1:
12

输入样例2:
5 4
1 2 1
2 3 2
3 1 3
4 5 4
输出样例2:
Impossible

样例输入2图示:
在这里插入图片描述
显然,样例输入2不能保证畅通,因此输出Impossible。

样例输入1图示:
在这里插入图片描述
乍一看,样例输入1很复杂。我们一步一步来进行分析。由图显而易见,该输入数据能保证畅通。样例输出1:12是该图最小生成树各边的权值之和。

由题意知:此题要求畅通工程所需的最低成本。即保证图中各顶点连通的前提下,还需要满足成本最低,这有一丝求图的最小生成树的意味。构造最小生成树的算法,我们熟知的有Prim算法和Kruskal算法两种。

下面给出运用kruskal算法的解法。(具体步骤请见下方代码)

老样子,先上代码:

#include <stdio.h>
#include <stdlib.h>//qsort()函数的头文件。

#define MAXVERTEXNUM 1001//城镇数目N(1<N≤1000)。
#define MAXEDGENUM 3001 //候选道路数目M≤3N。

/*typedef定义新的数据类型,相当于给该结构体取一个别名Road,该结构体类型即Road类型,
与此同时,定义了一个名为road的指针类型指向该结构体。
该结构体中的成员v1代表连通的城镇中其中一个城镇的编号,
               v2代表连通的城镇中另外一个城镇的编号,
               cost代表连通的城镇道路改建的预算成本。 */
typedef struct{
    
    
	int v1;
	int v2;
	int cost;
}Road,*road;

Road a[MAXEDGENUM];//定义了一个Road类型的数组,该数组名为a。
int parent[MAXVERTEXNUM];//定义了一个int类型的数组,该数组名为parent。

/*并查集之初始化操作*/
void init(int n)
{
    
    
 int i;
 for(i=1;i<=n;i++)
  	parent[i]=i;//城镇编号从1到N,将每个顶点(城镇)的父结点设置为自己。
}

/*并查集之寻找根节点操作*/
int find_root(int x)
{
    
    
	if(x==parent[x])//如果该元素的父结点是其自己,则说明该元素为根结点,返回该元素的编号。
	 return x;
	else
	/*否则递归地去寻找该元素的根结点,
	递归操作顺便把从根到该结点路径上的所有结点都直接放到根节点下。*/
	 return parent[x]=find_root(parent[x]);//压缩路径
	 //补充:压缩路径的好处:有可能降低了树高,提高以后的查询效率。
}

/*qsort()比较函数,该函数名为cmp。
根据结构体中的关键字cost,对结构体进行升序排序。*/
int cmp(const void *a,const void *b)
{
    
    
	road pa=(road)a;
	road pb=(road)b;
	int num1=pa->cost;
	int num2=pb->cost;
	return num1-num2;
}

int main()
{
    
    
	int N,M,i,sum=0,count=0;
	scanf("%d%d",&N,&M);//输入数据城镇数目N和候选道路数目M。
	
	for(i=0;i<M;i++)
	{
    
    
		scanf("%d%d%d",&a[i].v1,&a[i].v2,&a[i].cost);
		/*随后的M行,每行给出3个正整数,
		分别是该条道路直接连通的两个城镇的编号(从1编号到N)以及该道路改建的预算成本。*/
	}
	
	qsort(a,M,sizeof(Road),cmp);//qsort()函数对a数组根据结构体的关键字cost进行升序排序。*/
	
	init(N);//初始化操作。

    /*查找连通城镇中其中一个城镇的父结点
      和另外一个城镇的父结点。如果它们的父结点不同,则对它们进行合并操作,并将该结构体的cost关键字
      加到变量sum中。(kruskal算法的主体部分)*/
	for(i=0;i<M;i++)
	{
    
    
	  int A=find_root(a[i].v1);
	  int B=find_root(a[i].v2);
	  if(A!=B)
	  {
    
    
	  	parent[A]=B;
	  	sum+=a[i].cost;
	  }	
	}
	
	/*从第一个结点开始,查找其父结点是否是其本身。如果是的话,则对变量count进行自增操作。*/
	for(i=1;i<=N;i++)
	{
    
    
		if(parent[i]==i)
		 count++;
	}
    
    /*判断变量count是否为1,如果变量count为1,则说明该图是一个连通图,
    输入数据能保证城镇畅通,输出畅通工程需要的最低成本sum;
    否则,说明输入数据不足以保证城镇畅通,输出Impossible。*/
    if(count!=1)
     printf("Impossible");
    else
     printf("%d",sum);
	return 0;
}

总体而言,该题主要考察Kruskal算法,总体而言,是一道模板题。
本次分享就先到这,如果有任何疑问的地方,欢迎大家在评论区留言。感谢大家的捧场!

猜你喜欢

转载自blog.csdn.net/qq_46139801/article/details/113816510