蓝桥杯历届试题 合根植物 并查集 java实现

                                                              历届试题 合根植物

问题描述

  w星球的一个种植园,被分成 m * n 个小格子(东西方向m行,南北方向n列)。每个格子里种了一株合根植物。
  这种植物有个特点,它的根可能会沿着南北或东西方向伸展,从而与另一个格子的植物合成为一体。

  如果我们告诉你哪些小格子间出现了连根现象,你能说出这个园中一共有多少株合根植物吗?

输入格式

  第一行,两个整数m,n,用空格分开,表示格子的行数、列数(1<m,n<1000)。
  接下来一行,一个整数k,表示下面还有k行数据(0<k<100000)
  接下来k行,第行两个整数a,b,表示编号为a的小格子和编号为b的小格子合根了。


  格子的编号一行一行,从上到下,从左到右编号。
  比如:5 * 4 的小格子,编号:
  1 2 3 4
  5 6 7 8
  9 10 11 12
  13 14 15 16
  17 18 19 20

样例输入

5 4
16
2 3
1 5
5 9
4 8
7 8
9 10
10 11
11 12
10 14
12 16
14 18
17 18
15 19
19 20
9 13
13 17

样例输出

5

样例说明

  其合根情况参考下图

解析:

首先,题目思路很清晰,就是找图中结点由几个联通块。

其次,本来想用dfs的,但是一看点数,我去,1000000,(dfs一般时间复杂度为O(n^2))算了,只能套并查集了。

再者,再次分析,用并查集也挺简单的,只需要返回一个count即树的个数就ok了。

想要深层次了解并查集,我找了一篇博客,可以访问以下链接:

https://blog.csdn.net/dm_vincent/article/details/7655764

最后,经过两次不同的比较,我发现BufferedReader真的要比Scanner要快。

话不多说,上代码:



import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {

	//合根植物
	
	final static int maxn=1000000;
	static int root[]=new int[maxn+5];
	static int count=0;//树的数量
	static int find(int i)//返回i的根节点
	{
		while(i!=root[i])
		{
			i=root[i];//找根节点 根节点的根是自己
		}
		return i;
	}
	
	static void union(int i,int j)
	{
		int ri=find(i);
		int rj=find(j);
		if(ri!=rj)
			{	
				root[rj]=ri;//rj的根节点变为ri 从而实现合并
				count--;//此时树的数量减1
			}
		return;
	}
	public static void main(String[] args) throws IOException {
		int m,n,k;
		BufferedReader bfr=new BufferedReader(new InputStreamReader(System.in));
//加快输入
		String  str=bfr.readLine();
		String s[]=str.split(" ");
		m=Integer.parseInt(s[0]);
		n=Integer.parseInt(s[1]);
		str=bfr.readLine();
		s=str.split(" ");
		k=Integer.parseInt(s[0]);
		
		for(int i=1;i<=m*n;i++)//初始化,每一个结点的根结点都是自己
		{
			root[i]=i;
		}
		
		count=m*n;//开始时有多少个结点,就有多少棵树
		for(int i=1;i<=k;i++)
		{
			str=bfr.readLine();
			s=str.split(" ");
			int a=Integer.parseInt(s[0]);
			int b=Integer.parseInt(s[1]);
			union(a,b);
		}

		System.out.println(count);
	}

}

Scanner版: 



import java.io.IOException;
import java.util.Scanner;

//字符串输入700多毫秒,Scanner输入1.4s,证明BufferedReader输入确实要优于Scanner!!!
public class Main {

	//合根植物
	
		final static int maxn=1000000;
		static int root[]=new int[maxn+5];
		static int count=0;
		static int find(int i)//
		{
			while(i!=root[i]) i=root[i];//找根节点
			return i;
		}
		
		static void union(int i,int j)
		{
			int ri=find(i);
			int rj=find(j);
			if(ri!=rj)
				{	
					root[rj]=ri;
					count--;
				}
			return;
		}
		public static void main(String[] args) throws IOException {
			int m,n,k;
			Scanner cin=new Scanner(System.in);
			
			m=cin.nextInt();
			n=cin.nextInt();
			k=cin.nextInt();
			
			for(int i=1;i<=m*n;i++) root[i]=i;
		
			count=m*n;
			for(int i=1;i<=k;i++)
			{
				int a=cin.nextInt();
				int b=cin.nextInt();
				union(a,b);
			}
			System.out.println(count);
		}
}

两份代码都过了,现在展示BufferedReader的评测情况:

详细记录
评测点序号 评测结果 得分 CPU使用 内存使用 下载评测数据
1 正确 16.67 171ms 24.44MB 输入 输出
2 正确 16.67 171ms 24.28MB VIP特权
3 正确 16.67 703ms 58.41MB VIP特权
4 正确 16.67 765ms 58.92MB VIP特权
5 正确 16.67 718ms 42.30MB VIP特权
6 正确 16.67 609ms 54.62MB VIP特权

最优版:加了路径压缩,与合并时小树加到大树上:



import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {

	//合根植物
	final static int maxn=1000000;
	static int root[]=new int[maxn+5];
	static int size[]=new int[maxn+5];
	static int count=0;//树的数量
	static int find(int i)//返回i的根节点
	{
		while(i!=root[i])
		{
			root[i]=root[root[i]]; // 路径压缩,会破坏掉当前节点的父节点的尺寸信息,因为压缩后,当前节点的父节点已经变了
			i=root[i];//找根节点 根节点的根是自己
		}
		return i;
	}
	
	static void union(int i,int j)
	{
		int ri=find(i);
		int rj=find(j);
		if(ri!=rj)
			{	
				if(size[ri]>size[rj])//小树加到大树上,可以使树的深度较大树加小树上增长的更慢
				{
					root[rj]=ri;//rj的根节点变为ri 从而实现合并
					size[ri]+=size[rj];
				}
				else
				{
					root[ri]=rj;
					size[rj]+=size[ri];
				}
				count--;//此时树的数量减1
			}
		return;
	}
	public static void main(String[] args) throws IOException {
		int m,n,k;
		BufferedReader bfr=new BufferedReader(new InputStreamReader(System.in));//加快输入
		String  str=bfr.readLine();
		String s[]=str.split(" ");
		m=Integer.parseInt(s[0]);
		n=Integer.parseInt(s[1]);
		str=bfr.readLine();
		s=str.split(" ");
		k=Integer.parseInt(s[0]);
		
		for(int i=1;i<=m*n;i++)//初始化,每一个结点的根结点都是自己
		{
			root[i]=i;
			size[i]=1;
		}
		
		count=m*n;//开始时有多少个结点,就有多少棵树
		for(int i=1;i<=k;i++)
		{
			str=bfr.readLine();
			s=str.split(" ");
			int a=Integer.parseInt(s[0]);
			int b=Integer.parseInt(s[1]);
			union(a,b);
		}

		System.out.println(count);
	}

}

评测详情,与前面的比较:

详细记录

评测点序号 评测结果 得分 CPU使用 内存使用 下载评测数据
1 正确 16.67 296ms 27.93MB 输入 输出
2 正确 16.67 125ms 27.96MB VIP特权
3 正确 16.67 515ms 64.77MB VIP特权
4 正确 16.67 500ms 64.00MB VIP特权
5 正确 16.67 703ms 45.46MB VIP特权
6 正确 16.67 687ms 56.96MB VIP特权

最后,我想说的并查集真的一点都不难,而且平均时间复杂度就本题而言远小于O(n^2)!

猜你喜欢

转载自blog.csdn.net/Look_star/article/details/88208812
今日推荐