GDUT_寒假训练题解报告_图论专题_个人题解报告——题目:I - 还是畅通工程

GDUT_寒假训练题解报告_图论专题_个人题解报告——题目:I - 还是畅通工程

(更新:我用了Floyd+贪心,然而无意之中做出来了prim算法orz,无心插柳柳成荫)

某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。

Input

测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。
当N为0时,输入结束,该用例不被处理。

Output

对每个测试用例,在1行里输出最小的公路总长度。

Sample Input

3
1 2 1
1 3 2
2 3 4
4
1 2 1
1 3 4
1 4 1
2 3 3
2 4 2
3 4 5
0

Sample Output

3
5

读题完了之后,Floyd算法和dijkstra算法板子写过几遍的我先想到的是floyd+贪心,然后乱搞一下就搞出来,这个floyd板子不用怎么说,看代码:

for(int time=0;time<n;time++)
		{
			for(int time1=0;time1<n;time1++)
			{
				for(int time2=0;time2<n;time2++)
				{
					edge[time1][time2]=min(edge[time1][time2],edge[time1][time]+edge[time][time2]);
					edge[time2][time1]=edge[time1][time2];

				}
			}
		}

主要是贪心这里的思路:我做了什么呢,有一点点dijkstra内味儿,因为dijkstra也是贪心搞出来的bellman-ford剪枝,他的思想是最短边,那我们可以看到这个图很多边,随便找一个点,我找了0点,并且把它used掉

fill(used,used+n,false);
		used[0]=true;

这个地方,我used掉了0,之后进入一个函数:

void dfs()
{
	int _MIN=INF;
	int point1;
	int point2;
	for(int time=0;time<n;time++)
	{
		if(used[time])//如果这个点在序列里面,那么就要从这个点找最短路径;
		{
			for(int time1=0;time1<n;time1++)
			{
				if(!used[time1]&&edge[time][time1]<_MIN){_MIN=edge[time][time1];point1=time;point2=time1;}
			}
		}
	}
	sum+=_MIN;
	//point1,point2 分别记录了连通块序列中的点到非通块中的点的距离最短的两个点;
	used[point2]=true;
	//每次dfs都能确定一个点,所以在主函数里面调用n-1次就行了
}

这个函数主体是两个for,第一个for会看到进去马上有一个判断,你这个time是否用过了?这个用过的意思就是我的注释里面:如果这个点在序列里面,那么就从这个点找最短路径,为了简化,我们从一开始看起:

只有一个0号点被我used掉,也就是我的序列里面,或者说连通块里面,只有一个0号点,从这个0号点,已经有了floyd求出任意两点的最短路径,我用了贪心手段:

结果肯定会是一个无环图,因为有环对于连通块来说增加了一个无用的边。
我们设:现在有一个1——(n-1)为一个连通块,而且已经达到了最短边权和,满足条件:那么0号点他要和这个大连通块的所有点达成联系,只能是MIN=(MIN,edge[0][k]),k是1——(n-1)这n-1个点,现在MIN就是0到达这个连通块的最短路径了,

那根据我两个for,可以找出已经构成的(0——k个)这样的连通块,找到连通块中的每一个点到达非连通块中的点的最小值,然后再从这些点中取最小,那么每次都有这样的贪心算法,就可以每次执行这个函数都可以获得一个确定点,
这个算法有点近水楼台先得月的意味在。

完整代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <climits>
#include <queue>
#include <stack>

using namespace std;
#define INF 0x3f3f3f3f
#define ULL unsigned long long
int n;
int sum=0;
int edge[105][105];
bool used[105];
void dfs()
{
	int _MIN=INF;
	int point1;
	int point2;
	for(int time=0;time<n;time++)
	{
		if(used[time])//如果这个点在序列里面,那么就要从这个点找最短路径;
		{
			for(int time1=0;time1<n;time1++)
			{
				if(!used[time1]&&edge[time][time1]<_MIN){_MIN=edge[time][time1];point1=time;point2=time1;}
			}
		}
	}
	sum+=_MIN;
	//point1,point2 分别记录了连通块序列中的点到非通块中的点的距离最短的两个点;
	used[point2]=true;
	//每次dfs都能确定一个点,所以在主函数里面调用n-1次就行了
}


int main()
{
	while(~scanf("%d",&n)&&n)
	{
		sum=0;
		memset(edge,INF,sizeof(edge));
		for(int time=0;time<(n*(n-1))/2;time++)
		{
			int from,to,value;
			scanf("%d %d %d",&from,&to,&value);
			edge[from-1][to-1]=value;
			edge[to-1][from-1]=value;
		}
		//input finished
		//floyd
		for(int time=0;time<n;time++)
		{
			for(int time1=0;time1<n;time1++)
			{
				for(int time2=0;time2<n;time2++)
				{
					edge[time1][time2]=min(edge[time1][time2],edge[time1][time]+edge[time][time2]);
					edge[time2][time1]=edge[time1][time2];

				}
			}
		}
		fill(used,used+n,false);
		used[0]=true;
		for(int time=0;time<n-1;time++)dfs();
		printf("%d\n",sum);

	}
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章