7-8 朋友圈 (25 分)
某学校有N个学生,形成M个俱乐部。每个俱乐部里的学生有着一定相似的兴趣爱好,形成一个朋友圈。一个学生可以同时属于若干个不同的俱乐部。根据“我的朋友的朋友也是我的朋友”这个推论可以得出,如果A和B是朋友,且B和C是朋友,则A和C也是朋友。请编写程序计算最大朋友圈中有多少人。
输入格式:
输入的第一行包含两个正整数N(≤30000)和M(≤1000),分别代表学校的学生总数和俱乐部的个数。后面的M行每行按以下格式给出1个俱乐部的信息,其中学生从1~N编号:
第i个俱乐部的人数Mi(空格)学生1(空格)学生2 … 学生Mi
输出格式:
输出给出一个整数,表示在最大朋友圈中有多少人。
输入样例:
7 4
3 1 2 3
2 1 4
3 5 6 7
1 6
输出样例:
4
思路:第一,首先找到同组的,然后把同组的id值变为一样的,然后继续寻找是否有同组的其中的元素,举列子,以这道题为例,因为第一组第一个学生编号为1,所以id值为1,所以其他的两个学生编号的id值也是1,这样他们三个是同一个集合,现在寻找其他集合中是否有id值为1的,然后把剩下的元素id变为1,这样就全部合并了。
反思和总结:拿到此题,发现是并查集,于是去看了并查集,然后发现,看过还是操作有点困难,然后写了这道题目,在我博客的上一篇当中有并查集的几种方法的总结。然后非想用写并查集的类来操作,于是写了一个并查集的实现。期间数组总是越界,因为开始定义的都是初始长度,也就是学生的人数,发现索引和值不匹配,于是改了 n次,最后成功,把初始值传进来时候加一数组长度变为8,这样就可以把0置为0,a[7]=7,千方百计得出了答案,发现最后一个测试点还是超时。
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args){
Scanner sr = new Scanner(System.in);
int n = sr.nextInt(); //多少个学生
int m = sr.nextInt();//多少组朋友圈
UnionFind su = new UnionFind(n+1);
for (int i=0;i<m;i++){
int a = sr.nextInt(); //每组多少人数
int b = sr.nextInt(); //开始输入第一个学生编号
a--;
for (int j=1;j<=a;j++){
int c = sr.nextInt();//第二个学生编号
su.unionElements(b,c); //开始连接 循环链接 第一个 到第二个,第一个和第三个,只要每组有多少人都会连接
}
}
su.results();
}
}
class UnionFind{
private int [] id ;//初始定义的数组
public UnionFind(int size){
id = new int[size];
for (int i=0;i<id.length;i++){
id[i]=i;//把每个id相当于集合的索引设置初始的值
}
}
public int getSize() {
return id.length;
}
//查找元素p所对应的集合编号
public int find(int p){
if (p <0 && p>=id.length)
throw new IllegalArgumentException("p is out of bound");
return id[p];
}
//用于查看元素p和元素q是否属于同一个集合
public boolean isConnected(int p, int q) {
return find(p)==find(q);
}
//
public void unionElements(int p, int q) {
int pid = find(p);
int qid = find(q);
if (pid == qid) {
return;
}
for (int i = 0; i < id.length; i++) {
if (id[i] == pid) {
id[i] = qid;
}
}
}
public void results(){
//进行排序找到每个对应的第一个元素
for (int i = 1; i < id.length; i++) {
id[i] = find(i); //查找元素对应的编号
}
Arrays.sort(id);//对数组进行排序
int sum = 1,maxn = 0;
for (int i=2;i<id.length;i++){
if (id[i]==id[i-1]){
sum++; //遇到值相等加一
}else {
maxn = Math.max(sum, maxn); //进行比较
}
}
System.out.println(maxn);
}
}
第二个方法 我放在一个类当中,只用了并查集的两个方法,发现依旧超时:虽然代码优化了一点吧!
import java.util.Arrays;
import java.util.Scanner;
public class demo {
private int [] id ;//初始定义的数组
public demo(int size){
id = new int[size];
for (int i=0;i<id.length;i++){
id[i]=i;//把每个id相当于集合的索引设置初始的值
}
}
//查找元素p所对应的集合编号
public int find(int p){
return id[p];
}
//用于查看元素p和元素q是否属于同一个集合
public boolean isConnected(int p, int q) {
return find(p)==find(q);
}
//
public void unionElements(int p, int q) {
int pid = find(p);
int qid = find(q);
if (pid == qid) {
return;
}
for (int i = 0; i < id.length; i++) {
if (id[i] == pid) {
id[i] = qid;
}
}
}
public void results(){
//进行排序找到每个对应的第一个元素
for (int i = 1; i < id.length; i++) {
id[i] = find(i); //查找元素对应的编号
}
Arrays.sort(id);
int sum = 1,maxn = 0;
for (int i=2;i<id.length;i++){
if (id[i]==id[i-1]){
sum++;
}else {
maxn = Math.max(sum, maxn); //进行比较
}
}
System.out.println(maxn);
}
public static void main(String[] args){
Scanner sr = new Scanner(System.in);
int n = sr.nextInt(); //多少个学生
int m = sr.nextInt();//多少组朋友圈
demo d = new demo(n+1);
for (int i=0;i<m;i++){
int a = sr.nextInt(); //每组多少人数
int b = sr.nextInt(); //开始输入第一个学生编号
a--;
for (int j=1;j<=a;j++){
int c = sr.nextInt();//第二个学生编号
d.unionElements(b,c); //开始连接 循环链接 第一个 到第二个,第一个和第三个,只要每组有多少人都会连接
}
}
d.results();
}
}
第二个方法(树):
import java.util.Arrays;
import java.util.Scanner;
public class Main {
private int [] parent ;//初始定义的数组
public Main(int size){
parent = new int[size];
for (int i=0;i<parent.length;i++){
parent[i]=i;//把每个parent相当于集合的索引设置初始的值
}
}
//查找元素p所对应的集合编号
public int find(int p){
while(p!=parent[p]){
p=parent[p];
}
return p;
}
public void unionElements(int p, int q) {
int pRoot = find(p);
int qRoot = find(q);
if (pRoot == qRoot) {
return;
}
parent[pRoot]=qRoot;//指向根节点即可
}
public void results(){
//进行排序找到每个对应的第一个元素
for (int i = 1; i < parent.length; i++) {
parent[i] = find(i); //查找元素对应的编号
}
Arrays.sort(parent);
int sum = 1,maxn = 0;
for (int i=2;i<parent.length;i++){
if (parent[i]==parent[i-1]){
sum++;
}else {
maxn = Math.max(sum, maxn); //进行比较
}
}
System.out.println(maxn);
}
public static void main(String[] args){
Scanner sr = new Scanner(System.in);
int n = sr.nextInt(); //多少个学生
int m = sr.nextInt();//多少组朋友圈
Main d = new Main(n+1);
for (int i=0;i<m;i++){
int a = sr.nextInt(); //每组多少人数
int b = sr.nextInt(); //开始输入第一个学生编号
a--;
for (int j=1;j<=a;j++){
int c = sr.nextInt();//第二个学生编号
d.unionElements(b,c); //开始连接 循环链接 第一个 到第二个,第一个和第三个,只要每组有多少人都会连接
}
}
d.results();
}
}
同样超时!!!
第三个方法 :改变了输入输出方式依旧超时:最后放弃!
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
public class Main {
private int [] parent ;//初始定义的数组
public Main(int size){
parent = new int[size];
for (int i=0;i<parent.length;i++){
parent[i]=i;//把每个parent相当于集合的索引设置初始的值
}
}
//查找元素p所对应的集合编号
public int find(int p){
while(p!=parent[p]){
p=parent[p];
}
return p;
}
public void unionElements(int p, int q) {
int pRoot = find(p);
int qRoot = find(q);
if (pRoot == qRoot) {
return;
}
parent[pRoot]=qRoot;//指向根节点即可
}
public void results(){
//进行排序找到每个对应的第一个元素
for (int i = 1; i < parent.length; i++) {
parent[i] = find(i); //查找元素对应的编号
}
Arrays.sort(parent);
int sum = 1,maxn = 0;
for (int i=2;i<parent.length;i++){
if (parent[i]==parent[i-1]){
sum++;
}else {
maxn = Math.max(sum, maxn); //进行比较
}
}
System.out.println(maxn);
}
public static void main(String[] args)throws IOException {
BufferedReader sr = new BufferedReader(new InputStreamReader(System.in));
String s = sr.readLine();
int n = Integer.parseInt(s.split(" ")[0]); //多少个学生
int m = Integer.parseInt(s.split(" ")[1]);//多少组朋友圈
Main d = new Main(n+1);
for (int i=0;i<m;i++){
String ss[] = sr.readLine().split(" ");
int a = Integer.parseInt(ss[0]); //每组多少人数
int b = Integer.parseInt(ss[1]); //开始输入第一个学生编号
a--;
for (int j=1;j<=a;j++){
int c = Integer.parseInt(ss[j+1]);//第二个学生编号
d.unionElements(b,c); //开始连接 循环链接 第一个 到第二个,第一个和第三个,只要每组有多少人都会连接
}
}
d.results();
}
}
第四中加入了树的节点判断,然而崩溃,还是超时,真放弃了!!!!!
import java.util.Arrays;
import java.util.Scanner;
public class Main {
private int [] parent ;//初始定义的数组
private int [] sz;
public Main(int size){
parent = new int[size];
sz = new int[size];
for (int i=0;i<parent.length;i++){
parent[i]=i;//把每个parent相当于集合的索引设置初始的值
sz[i]=1;
}
}
//查找元素p所对应的集合编号
public int find(int p){
while(p!=parent[p]){
p=parent[p];
}
return p;
}
public void unionElements(int p, int q) {
int pRoot = find(p);
int qRoot = find(q);
if (pRoot == qRoot) {
return;
}
if (sz[pRoot]<sz[qRoot]){
parent[pRoot]=qRoot;//p个几点所在的个数相对于比较少,让元素个数比较少的指向元素个数比较多的根节点
sz[qRoot]+=sz[pRoot];
}else{
parent[qRoot]=pRoot;
sz[pRoot]+=sz[qRoot];
}
}
public void results(){
//进行排序找到每个对应的第一个元素
for (int i = 1; i < parent.length; i++) {
parent[i] = find(i); //查找元素对应的编号
}
Arrays.sort(parent);
int sum = 1,maxn = 0;
for (int i=2;i<parent.length;i++){
if (parent[i]==parent[i-1]){
sum++;
}else {
maxn = Math.max(sum, maxn); //进行比较
}
}
System.out.println(maxn);
}
public static void main(String[] args){
Scanner sr = new Scanner(System.in);
int n = sr.nextInt(); //多少个学生
int m = sr.nextInt();//多少组朋友圈
Main d = new Main(n+1);
for (int i=0;i<m;i++){
int a = sr.nextInt(); //每组多少人数
int b = sr.nextInt(); //开始输入第一个学生编号
a--;
for (int j=1;j<=a;j++){
int c = sr.nextInt();//第二个学生编号
d.unionElements(b,c); //开始连接 循环链接 第一个 到第二个,第一个和第三个,只要每组有多少人都会连接
}
}
d.results();
}
}