HDU 1213 How Many Tables (DFS or 并查集)

写在这上面的是最新修改内容:

看了其他博主的并查集解决方法,发现可以利用递归实时压缩路径处理,放到HDU发现AC了。感慨良多,自己对并查集了解不够深,导致了写了个伪并查集并且TLE。下面贴一下模板代码:

//转载:https://blog.csdn.net/Ema1997/article/details/79820122
#include <cstdio>
#include <iostream>
#include <cstring>
#include <set>
#include <queue>

using namespace std;

const int maxn = 1010;

int n, m;
int par[maxn];

int get_par(int a)//递归找根节点,并且还把根节点返回做了压缩路径处理,这一步尤其精妙
{
    if (par[a] != a) par[a] = get_par(par[a]);
    return par[a];
}

int query(int a, int b)//查询两个节点是否同一个集合
{
    return get_par(a) == get_par(b);
}

void Merge(int a, int b)//合并集合操作
{
    par[get_par(a)] = get_par(b);
}

void Init()//并查集的初始化
{
    for (int i = 1; i <= n; i++) {
        par[i] = i;
    }
}

int main()
{
    #ifndef ONLINE_JUDGE
    freopen ("in.txt", "r", stdin);
    #endif // ONLINE_JUDGE
    int t;
    scanf ("%d", &t);
    while (t--) {
        scanf ("%d%d", &n, &m);
        Init ();
        for (int i = 0; i < m; i++) {
            int a, b;
            scanf ("%d%d", &a, &b);
            Merge (a, b);
        }
        int num = 0;
        for (int i = 1; i <= n; i++) {
            if (i == get_par(i)) {
                    num++;
            }
        }
        printf ("%d\n", num);
    }
}

——+——+——+——+——+——+——+——+——+——+——+——+——+——+——+——+——+——+——+——+——+

这道题,笔者用的是并查集来做的,还做了缩短查找路径的剪枝处理,但还是超时了。看了答案用的是二维数组+DFS。

相比之下,笔者的做法时间复杂度较高,但空间复杂度O(n);答案的做法是时间复杂度较低,空间复杂度O(n^2)。各有优劣。因此这题贴两份代码,第一份是DFS可以AC的,第二份是并查集+剪枝da但TLE的。

DFS版:

#include<string>
#include<iostream>
#include<string.h>
using namespace std;

int map[1002][1002];
int n,m;
int mark[1002];


void dfs(int num)
{

	for(int i=1;i<=n;i++)
	{
		if( map[num][i] && !mark[i] )
		{
			mark[i]=1;
			dfs(i);
		}
	}
}

int main()
{

	int t;
	cin>>t;
	while(t--)
	{

		int a,b;
		cin>>n>>m;
		memset(mark,0,sizeof(mark));
		memset(map,0,sizeof(map));
		for(int i=1;i<=m;i++)
		{
			cin>>a>>b;
			map[a][b]=1;
			map[b][a]=1;
		}
		int ans=0;
		for(int i=1;i<=n;i++)
		{
			if( !mark[i] )
			{
				ans++;
				mark[i]=1;
				dfs(i);
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}

并查集+剪枝版:

#include<stdio.h>
#include<stdlib.h>
#include <string.h>


#define MAXNUM 1001

int main()
{
	int z,n,m,a,b,x,y;
	int visit[MAXNUM]={0};
	scanf("%d",&z);
	while(z--)
	{
		scanf("%d%d",&n,&m);
		memset(visit,0,sizeof(visit));//重置关系表
		for(int i=0;i<m;i++)
		{
			scanf("%d%d",&a,&b);
			//处理数据
			if(visit[a]==0&&visit[b]==0){visit[b]=a;}//如果a和b都没有祖先,则b归属a
			else if(visit[a]==0&&visit[b]!=0)//如果a没祖先,则b以及b的祖先全归属a
			{
				while(visit[b]!=0)
				{
					y=visit[b];
					visit[b]=a;
					b=y;
				}
				visit[b]=a;
			}
			else if(visit[a]!=0&&visit[b]==0)//如果b没祖先,则a以及a的祖先全归属b
			{
				while(visit[a]!=0)
				{
					x=visit[a];
					visit[a]=b;
					a=x;
				}
				visit[a]=b;
			}
			else
			{//否则,递归查找各自祖先,祖先相同则忽略,祖先不同则将b和a的祖先全归属a的首祖先
				x=visit[a];
				y=visit[b];
				while(visit[x]!=0){x=visit[x];}
				while(visit[y]!=0){y=visit[y];}
				if(x!=y)
				{
					while(visit[b]!=0)//把b以及b的祖先全归属a的首祖先
					{
						y=visit[b];
						visit[b]=x;
						b=y;
					}
					visit[b]=x;
					while(visit[a]!=x)//把a的非首祖先全归属到a的首祖先上
					{
						y=visit[a];
						visit[a]=x;
						a=y;
					}
				}
			}
		}
		//计算数据
		int sum=0;
		for(int j=1;j<=n;j++)
		{//计算有多少个祖先首,即visit[j]==0
			if(visit[j]==0) sum++;
		}
		//输出数据
		printf("%d\n",sum);
	}
	//getchar();
	//getchar();
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值