【图论】畅通工程 ,我的思路 & 并查集自己的修改

helloDesword 2014-07-03 08:32:56
练习的题目是杭电里面浙大的题目。

http://acm.hdu.edu.cn/showproblem.php?pid=1232

题目描述:某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?

输入:测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。
注意:两个城市之间可以有多条道路相通,也就是说
3 3
1 2
1 2
2 1
这种输入也是合法的
当N为0时,输入结束,该用例不被处理。

输出:对每个测试用例,在1行里输出最少还需要建设的道路数目。

测试数据:
input
4 2
1 3
4 3
3 3
1 2
1 3
2 3
5 2
1 2
3 5
999 0
0


Sample Output
1
0
2
998


==================================================================
我的思路:为每个城市做一个标记数组。对于每一对输入,作如下处理。
                for(i=0 ; i< m ;i++)
{
scanf("%d%d",&a,&b);
//if there is no link in the set, add one road
if(city[a] != 1 && city[b] != 1 )
{
count++;
}
city[a] =1;
city[b] =1;
}

1代表此城市可联通,0表示不联通。
如果输入的两个城市有一个是已经标记的,即可联通的,则另一个城市也是可联通的,直接标记。
如果输入的两个城市都没有标记,则这是一个新产生的可联通集合,count表示需加入的道路数,增加一条路,将这两个城市标记为可联通的。
处理完输入后,对于没有标记的城市,每个都增加一条路,表示连到可联通集合中。
以上处理完成之后,就统计出最小的道路数了。
但是提交OJ,发现是wrong answer。 不知道是那种情况没有考虑到?
这次想请教的就是这个问题。漏了那种情况?

===========================================================

另:对于网上的一个题解,使用的是并查集,虽然目前对并查集不是很了解,但是看到其中一个核心的找父亲的函数:
int Getfa( int t )  //找父亲
{
if( fa[t] == t ) return t;
fa[t] = Getfa(fa[t]);
return fa[t];
}

通过这个函数就能够找到该点城市最终能够联通到的城市,看到是递归,我就像改成循环来处理,减少时间消耗:
int Getfa( int t )  //找父亲
{
while( fa[t] != t)
{
t= fa[t];
//printf("%d,",t);//output the load
}
return t;
}

但是提交之后,发现结果是超时了。 感觉上面实现的两个函数达到的目的应该都是一样的,为什么后面改编的不行呢?
如果fa数组每一个元素fa[t] != t 了,那么递归的那个函数也应该一直递归不停止吧。为什么递归的可以通过,改编的不能通过呢?


总共两个问题,麻烦大家帮忙了~
...全文
288 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
灌水号 2014-07-03
  • 打赏
  • 举报
回复
引用 2 楼 ls1160 的回复:
[quote=引用 1 楼 u014444402 的回复:] 第一个问题,漏了一种情况,就是如果处理完输入之后如果是两个或者多个独立的联通集,你少了各个联通集的连接。 第二个问题,你的返回值是与原函数等价的,有一点不一样的就是原函数更新了fa[t],你没有更新,这样对后面的处理有没有影响。
针对第一个问题,我应该是对多个独立的集合有考虑的。 比如:总共5个城市。 对于输入 1 2 3 5 第一次构造集合{1 ,2 } 第二次构造集合时,因为集合中3和5都没有,即1,2 和 3,5 将是两个独立的集合, 因此需要将它们用一条路连接。因此我将count加1. 这样, 此种情况下构造的集合{1,2,3,5} 就是一个整体的集合。即在一边处理输入的时候,一边我就将独立集合连接到一起了。 我认为这样处理应该是合理的吧? 再次请教一下,谢谢~ 针对第一个问题,我应该是对多个独立的集合有考虑的。 比如:总共5个城市。 对于输入 1 2 3 5 第一次构造集合{1 ,2 } 第二次构造集合时,因为集合中3和5都没有,即1,2 和 3,5 将是两个独立的集合, 因此需要将它们用一条路连接。因此我将count加1. 这样, 此种情况下构造的集合{1,2,3,5} 就是一个整体的集合。即在一边处理输入的时候,一边我就将独立集合连接到一起了。 我认为这样处理应该是合理的吧? 再次请教一下,谢谢~ [/quote] 如果你输入5个城市 再输入 1 2 3 5 按照你的程序 1 2 count = 1 3 5 count =2 这时你认为1 2 3 5是联通的,其实没有联通。
helloDesword 2014-07-03
  • 打赏
  • 举报
回复
引用 1 楼 u014444402 的回复:
第一个问题,漏了一种情况,就是如果处理完输入之后如果是两个或者多个独立的联通集,你少了各个联通集的连接。 第二个问题,你的返回值是与原函数等价的,有一点不一样的就是原函数更新了fa[t],你没有更新,这样对后面的处理有没有影响。
针对第一个问题,我应该是对多个独立的集合有考虑的。 比如:总共5个城市。 对于输入 1 2 3 5 第一次构造集合{1 ,2 } 第二次构造集合时,因为集合中3和5都没有,即1,2 和 3,5 将是两个独立的集合, 因此需要将它们用一条路连接。因此我将count加1. 这样, 此种情况下构造的集合{1,2,3,5} 就是一个整体的集合。即在一边处理输入的时候,一边我就将独立集合连接到一起了。 我认为这样处理应该是合理的吧? 再次请教一下,谢谢~ 针对第一个问题,我应该是对多个独立的集合有考虑的。 比如:总共5个城市。 对于输入 1 2 3 5 第一次构造集合{1 ,2 } 第二次构造集合时,因为集合中3和5都没有,即1,2 和 3,5 将是两个独立的集合, 因此需要将它们用一条路连接。因此我将count加1. 这样, 此种情况下构造的集合{1,2,3,5} 就是一个整体的集合。即在一边处理输入的时候,一边我就将独立集合连接到一起了。 我认为这样处理应该是合理的吧? 再次请教一下,谢谢~
灌水号 2014-07-03
  • 打赏
  • 举报
回复
第一个问题,漏了一种情况,就是如果处理完输入之后如果是两个或者多个独立的联通集,你少了各个联通集的连接。 第二个问题,你的返回值是与原函数等价的,有一点不一样的就是原函数更新了fa[t],你没有更新,这样对后面的处理有没有影响。
helloDesword 2014-07-03
  • 打赏
  • 举报
回复
引用 9 楼 IAccepted 的回复:
虽然查找根节点是递归啊,但是在返回的时候更新每个节点的根节点了,这样就路径压缩了,对以后的查询平均深度只有2啊,这个递归很简单关键就是路径压缩的思想要想到。
恩恩,的确。
iaccepted 2014-07-03
  • 打赏
  • 举报
回复
虽然查找根节点是递归啊,但是在返回的时候更新每个节点的根节点了,这样就路径压缩了,对以后的查询平均深度只有2啊,这个递归很简单关键就是路径压缩的思想要想到。
helloDesword 2014-07-03
  • 打赏
  • 举报
回复
引用 7 楼 u014444402 的回复:
[quote=引用 5 楼 ls1160 的回复:] [quote=引用 4 楼 u014444402 的回复:]
漏了一种情况我补充了一下。[/quote] 那个地方不对
for ( j=0; j<n; j++ )
                    if ( city[j] == temp ) city[j] = city[b];
这里的 j的范围不对。[/quote] 对的,就是这里!!! 十分感谢! 看来我还是要多练习呀, 思维不够严谨的。
灌水号 2014-07-03
  • 打赏
  • 举报
回复
引用 5 楼 ls1160 的回复:
[quote=引用 4 楼 u014444402 的回复:]
我了解你的思路了,发现我遗漏的情况在于,如果后期输入的城市对里面,存在将两个独立集合连在一起的路,那么独立集合应当减少这种情况。 但是我修改之后,系统仍提示是wrong answer。然后不太明白。
#include <stdio.h>

int city[1005];

int main()
{	
	int n,m;
	int count=0;
	while(1)
	{
		scanf("%d",&n);
		if( n == 0)
		{
			break;
		}		
		scanf("%d",&m);
		int i,a,b,temp,j;
		int unionIndex = 0;
		int unionNum = 0;
		for ( i=1; i<=n; i++ ) city[i] = -1;//2、城市编号从1开始
		for ( i=0; i<m; i++ )
		{
		    scanf("%d%d",&a,&b);
		    if ( a == b ) continue;
		
		    if ( (city[a] == -1) && ( city[b] == -1 ) )
		    {
		        city[a] = unionIndex;
		        city[b] = unionIndex++;
		        unionNum++;
		    }
		    else if ( (city[a] != -1) && ( city[b] == -1 ) )
		    {
		        city[b] = city[a];
		    }
		    else if ( (city[a] == -1) && ( city[b] != -1 ) )
		    {
		        city[a] = city[b];
		    }
		    //缺少对后期连接了两个集合的判断,  可以减少独立集合数 
		    //1、缺少对  输入的两个数本身就是一个集合的判断,这时不需要减少独立集合数 
		    else if ( (city[a] != city[b]) && (city[a] != -1) && ( city[b] != -1 ) )
		    {
		    	//对所有等于a集合标志的元素,归并成b集合标志的元素 
		        temp = city[a];
		        for ( j=0; j<n; j++ )
		            if ( city[j] == temp ) city[j] = city[b];
		        unionNum--;
		    }
		}
		
		for ( i=1; i<=n; i++ )
		{
		    if ( city[i] == -1 ) unionNum++;
		}
		//
		//到目录为止一共有unionNum个联通集,则至少需要再添unionNum-1条路
		printf( "%d\n", unionNum-1 );		
	}	
	return 0;
}
漏了一种情况我补充了一下。[/quote] 那个地方不对
for ( j=0; j<n; j++ )
                    if ( city[j] == temp ) city[j] = city[b];
这里的 j的范围不对。
灌水号 2014-07-03
  • 打赏
  • 举报
回复
引用 5 楼 ls1160 的回复:
[quote=引用 4 楼 u014444402 的回复:]
我了解你的思路了,发现我遗漏的情况在于,如果后期输入的城市对里面,存在将两个独立集合连在一起的路,那么独立集合应当减少这种情况。 但是我修改之后,系统仍提示是wrong answer。然后不太明白。
#include <stdio.h>

int city[1005];

int main()
{	
	int n,m;
	int count=0;
	while(1)
	{
		scanf("%d",&n);
		if( n == 0)
		{
			break;
		}		
		scanf("%d",&m);
		int i,a,b,temp,j;
		int unionIndex = 0;
		int unionNum = 0;
		for ( i=1; i<=n; i++ ) city[i] = -1;//2、城市编号从1开始
		for ( i=0; i<m; i++ )
		{
		    scanf("%d%d",&a,&b);
		    if ( a == b ) continue;
		
		    if ( (city[a] == -1) && ( city[b] == -1 ) )
		    {
		        city[a] = unionIndex;
		        city[b] = unionIndex++;
		        unionNum++;
		    }
		    else if ( (city[a] != -1) && ( city[b] == -1 ) )
		    {
		        city[b] = city[a];
		    }
		    else if ( (city[a] == -1) && ( city[b] != -1 ) )
		    {
		        city[a] = city[b];
		    }
		    //缺少对后期连接了两个集合的判断,  可以减少独立集合数 
		    //1、缺少对  输入的两个数本身就是一个集合的判断,这时不需要减少独立集合数 
		    else if ( (city[a] != city[b]) && (city[a] != -1) && ( city[b] != -1 ) )
		    {
		    	//对所有等于a集合标志的元素,归并成b集合标志的元素 
		        temp = city[a];
		        for ( j=0; j<n; j++ )
		            if ( city[j] == temp ) city[j] = city[b];
		        unionNum--;
		    }
		}
		
		for ( i=1; i<=n; i++ )
		{
		    if ( city[i] == -1 ) unionNum++;
		}
		//
		//到目录为止一共有unionNum个联通集,则至少需要再添unionNum-1条路
		printf( "%d\n", unionNum-1 );		
	}	
	return 0;
}
漏了一种情况我补充了一下。[/quote] 把while循环去掉试试。
helloDesword 2014-07-03
  • 打赏
  • 举报
回复
引用 4 楼 u014444402 的回复:
我了解你的思路了,发现我遗漏的情况在于,如果后期输入的城市对里面,存在将两个独立集合连在一起的路,那么独立集合应当减少这种情况。 但是我修改之后,系统仍提示是wrong answer。然后不太明白。
#include <stdio.h>

int city[1005];

int main()
{	
	int n,m;
	int count=0;
	while(1)
	{
		scanf("%d",&n);
		if( n == 0)
		{
			break;
		}		
		scanf("%d",&m);
		int i,a,b,temp,j;
		int unionIndex = 0;
		int unionNum = 0;
		for ( i=1; i<=n; i++ ) city[i] = -1;//2、城市编号从1开始
		for ( i=0; i<m; i++ )
		{
		    scanf("%d%d",&a,&b);
		    if ( a == b ) continue;
		
		    if ( (city[a] == -1) && ( city[b] == -1 ) )
		    {
		        city[a] = unionIndex;
		        city[b] = unionIndex++;
		        unionNum++;
		    }
		    else if ( (city[a] != -1) && ( city[b] == -1 ) )
		    {
		        city[b] = city[a];
		    }
		    else if ( (city[a] == -1) && ( city[b] != -1 ) )
		    {
		        city[a] = city[b];
		    }
		    //缺少对后期连接了两个集合的判断,  可以减少独立集合数 
		    //1、缺少对  输入的两个数本身就是一个集合的判断,这时不需要减少独立集合数 
		    else if ( (city[a] != city[b]) && (city[a] != -1) && ( city[b] != -1 ) )
		    {
		    	//对所有等于a集合标志的元素,归并成b集合标志的元素 
		        temp = city[a];
		        for ( j=0; j<n; j++ )
		            if ( city[j] == temp ) city[j] = city[b];
		        unionNum--;
		    }
		}
		
		for ( i=1; i<=n; i++ )
		{
		    if ( city[i] == -1 ) unionNum++;
		}
		//
		//到目录为止一共有unionNum个联通集,则至少需要再添unionNum-1条路
		printf( "%d\n", unionNum-1 );		
	}	
	return 0;
}
漏了一种情况我补充了一下。
灌水号 2014-07-03
  • 打赏
  • 举报
回复

int unionIndex = 0;
int unionNum = 0;
for ( i=0; i<n; i++ ) city[i] = -1;//假设城市编号从0开始
for ( i=0; i<m; i++ )
{
    scanf("%d%d",&a,&b);
    if ( a == b ) continue;

    if ( (city[a] == -1) && ( city[b] == -1 ) )
    {
        city[a] = unionIndex;
        city[b] = unionIndex++;
        unionNum++;
    }
    else if ( (city[a] != -1) && ( city[b] == -1 ) )
    {
        city[b] = city[a];
    }
    else if ( (city[a] == -1) && ( city[b] != -1 ) )
    {
        city[a] = city[b];
    }
    else if ( (city[a] != -1) && ( city[b] != -1 ) )
    {
        temp = city[a];
        for ( j=0; j<n; j++ )
            if ( city[j] == temp ) city[j] = city[b];
        unionNum--;
    }
}

for ( i=0; i<n; i++ )
{
    if ( city[i] == -1 ) unionNum++;
}
//到目录为止一共有unionNum个联通集,则至少需要再添unionNum-1条路
printf( "%d ", unionNum-1 );

64,645

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧