题目地址
java和c++都有
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
给你一个 n 个点,m 条边的无向图,求至少要在这个的基础上加多少条无向边使得任意两个点可达~
输入描述:
第一行两个正整数 n 和 m 。
接下来的m行中,每行两个正整数 i 、 j ,表示点i与点j之间有一条无向道路。
输出描述:
输出一个整数,表示答案
示例1
输入
4 2
1 2
3 4
输出
1
备注:
对于100%的数据,有n,m<=100000。
/*
整体思路,用数组做并查集问题
根据题目要求创建数组pre,完成对数组的初始化(这道题下标从1开始)
ps:若pre[3]=7;那么视为3的上级是7 下标不可重复,值可重复,对应一个父节点可以对应多个子节点
默认无边,每个节点都是自己的主元
默认要加n-1条边(题目保证输入是正整数,不需要考虑0的情况)用total计数
然后每录入一条边的数组,将边加入图中,如果属于同一个连通分支total不变(加了一个环)
如果是不同分支,那么total--
total就是需要加的边
*/
import java.util.Scanner;
public class Main {//并查集
static int pre[]=new int[100001];
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int m=input.nextInt();
init(pre, n);
int total=n-1;
while (m>0) {
int x=input.nextInt();
int y=input.nextInt();
int f1=find(x);
int f2=find(y);
if (f1!=f2) {
pre[f1]=f2;
total--;
}
m--;
}
System.out.println(total);
}
//初始化
public static void init(int pre[],int n){
for (int i = 1; i <= n; i++) {
pre[i]=i;
}
}
//寻找主元
public static int find(int x) {
int r=x;
while (pre[r]!=r) {
r=pre[r];
}
return r;
}
//加边
public static void join(int x,int y){
x=find(x);
y=find(y);
if(x!=y){
pre[x]=y;//x入赘到y上
}
}
//补充的内容
/*
* 在这道题,因为没有层次关系,可以在查找的时候用路径压缩来优化算法,
* 减少查找主元的次数,具体过程就是让子节点找到后直接与主元相连
*
* 非递归写法(压缩路径)
*/
public static int find2(int x) {
int r=x;
while(pre[r]!=r){//自己不是代表元
r=pre[r]; //让自己的上级再去找上级
}
int i=x;int j;
while(i!=r){
j=pre[i];
pre[i]=r;
i=j;
}
return r;
}
//数组用递归的方式寻找主元
public static int find3(int x){//数组 递归方法找主元
return pre[x]!=x?pre[x]=find3(pre[x]) : x;
}
//数组递归写法2
//递归写法
public static int find4(int x)
{
if(pre[x] != x)
{
pre[x] = find(pre[x]); //找的祖先递归赋值,将本次查找经过的每个节点都直接连到祖先节点上
}
return pre[x];
}
}
#include<iostream>
#include<cstdio>
int pre[100000];//根据题目的数据规模
using namespace std;
//并查集 --加边无向图
//若pre[3]=7;那么视为3的上级是7 下标不可重复,值可重复,对应一个父节点可以对应多个子节点
int find(int x) {
int r=x;
while(pre[r]!=r){//自己不是代表元
r=pre[r]; //让自己的上级再去找上级
}
/* int i=x;int j;
while(i!=r){
j=pre[i];
pre[i]=r;
i=j;
}*/
return r;
}
void join(int x,int y){
int fx=find(x),fy=find(y);
if(fx!=fy){
pre[fx]=fy;//x入赘到y上
}
}
void init(int pre[],int n){//自己是自己的代表元
for(int i=1;i<=n;i++){
pre[i]=i;
}
}
int main(){
int n,m ,p1,p2,i,total,f1,f2;
scanf("%d %d",&n,&m);//正整数已经确保n!=0
init(pre,n);
total=n-1;
//join函数修改以适应题目
while(m--){
scanf("%d %d",&p1,&p2);
f1=find(p1);
f2=find(p2);
if(f1!=f2){
pre[f1]=f2;
total--;
}
}
printf("%d\n",total);
return 0;
}