多种方法解决”图+深搜“编程题目

Problem Description
  多多终于从小学升入了初中。新班级共有n人,学号分别为从1~n。其中多多的学号是1号。
  新班级里有m对同学是事先就相互认识的,其他的同学互相都不认识。
  多多新班级里所有的同学(包括多多在内)都非常害羞,如果两个同学不认识,那么必须要由一个同时认识这两名同学的人来介绍他们认识,否则他们就会一直互相不认识。
  现在你已经知道了这m对相互认识的同学的信息。请你写一个程序,来计算一下多多最多可以认识多少名同学。
Input
  输入的第一行包含一个数字T,代表测试数据的组数。
  每组数据第一行包含两个正整数n m,代表班级的人数和已知相互认识的关系数。
  以下m行每行包含两个数字x y,代表学号为x的同学和学号为y的同学相互认识。
  数据保证n小于100,m小于3000,x y 均小于等于n。
Output
  每组输出占用一行,仅包含一个数字,即多多最多可以认识的同学数。
Sample Input
2
5 3
1 3
2 5
3 4
10 7
1 6
2 5
2 6
3 4
4 6
6 7
8 9
Sample Output
2
6




一、第一种做法:ArrayList+Stack
这道题第一反应的办法就是图+深搜,但当初做这题时还不会用,但又不想放弃,便试着用ArrayList+Stack给折腾出来了。

以下是代码:
import java.util.ArrayList;
import java.util.Scanner;
import java.util.Stack;
/**
 * @author MoonMonster
 * @date 2015-7-10 下午03:50:54
 */
public class Main {
	public static void main(String[] args) {
		int n; // 班级人数
		int m; // 认识对数
		int T; // 测试数
		int a, b;
		Scanner sc = new Scanner(System.in);
		T = sc.nextInt();
		while (true) {
			int result = 0;			//计算结果
			if (T == 0) {
				break;
			}
			//创建集合和对象
			//arr中存储除了含有多多的之外的每一组的数据
			ArrayList<Student> arr = new ArrayList();
			//stack中存储与多多认识但还没有对他认识的人进行查找的数据
			Stack<Integer> stack = new Stack();
			//临时变量,存储从stack中remove的数据
			ArrayList<Student> _arr = new ArrayList();
			//存储已经入栈过的数据
			ArrayList<Integer> person = new ArrayList();
			
			//输入认识的人的对
			n = sc.nextInt();
			m = sc.nextInt();
			//存储一组数据
			Student[] st = new Student[m];
			for (int i = 0; i < m; i++) {
				st[i] = new Student();
				a = sc.nextInt();
				b = sc.nextInt();
				st[i].x = a;
				st[i].y = b;
				//将每一组数据加入集合
				arr.add(st[i]);
				//遍历从1(多多)开始
				if (a == 1 || b == 1) {
					if (a == 1) {
						//将多多认识的人的数据放入栈中
						stack.push(b);
						//表示该数据已经入过栈
						person.add(b);
					} else {
						stack.push(a);
						person.add(a);
					}
					//多多认识的人+1
					result++;
					//将含有1的数据对移除
					arr.remove(st[i]);
				}
			}
			//如果栈里有数据,则一直遍历
			while (!stack.empty()) {
				//获得栈中的数据,与集合中的数据进行比较
				//栈中存的都是多多认识的人
				int c = (int) stack.pop();
				for (int i = 0; i < arr.size(); i++) {
					Student _st = arr.get(i);
					if (c == _st.x || c == _st.y) {
						if (c == _st.x) {
							//判断要入栈的数是否已经入过栈
							//将C认识的人(即多多会认识的人)入栈
							if(!person.contains(_st.y)){
								result++;
								stack.push(_st.y);		//入栈
								person.add(_st.y);		//入栈,新加入的数据
							}
						} else {
							//判断要入栈的数是否已经入过栈
							if(!person.contains(_st.x)){
								result++;
								stack.push(_st.x);
								person.add(_st.x);
							}
						}
						//表示_st的数据已经查找过
						_arr.add(_st);
					}
				}
				for( int i=0; i<_arr.size(); i++ ){
					//剔除查找了的数据
					arr.remove(_arr.get(i));
				}
				_arr.clear();		//清空数据
			}
			if( n == 1 ){
				System.out.println(0);
			}else{
				System.out.println(result);
			}
			T --;
		}
	}
}
class Student {
	public int x;
	public int y;
}

稍微添上图解,写了好几个月了,刚开始看的时候都没看明白...再次深深明白注释的作用。


多种方法解决”图+深搜“编程题目_第1张图片


从stack中取出栈顶元素,然后去arr中匹配,若某一组数据有跟它一样的,且另一个在person中不存在,则把数据入栈,并且放进person中,result加1;同时在arr中把那组数据remove掉;循环直到栈空。



二、第二种做法:图+深搜
在看完图那章后,就一直想用图的知识把这题给做了,但却一直拖到今天,拖延症患者啊...
没有什么可以说的,直接贴代码:

package com.ct.graph;

import java.util.Scanner;
import java.util.Stack;

/** 
 * @author MoonMonster
 * @date 2015-10-16 下午02:46:38  
 */
//边
class Edge{
	//索引
	public int index;
	public Edge next;
}

//顶点
class VexNode{
	//数据
	public int data;
	public Edge firstedge;
}

//邻接表
class MyGraph{
	public int vexnum, arcnum;
	VexNode[] vexnode;
	
	public MyGraph(int n,int m){
		
		this.vexnum = n;
		this.arcnum = m;
		
		vexnode = new VexNode[n];
		for(int i=0; i<n; i++){
			vexnode[i] = new VexNode();
		}
	}
}

public class Main {
	static Scanner sc = new Scanner(System.in);
	
	public static void main(String[] args) {
		int n, m;
		int T;
		MyGraph graph = null;
		
		//测试数目
		T = sc.nextInt();
		
		while(true){
			if(T == 0){
				break;
			}
			T --;
			
			//总人数和组数
			n = sc.nextInt();
			m = sc.nextInt();
			graph = new MyGraph(n,m);
			createGraph(graph);
			//print(graph);
			System.out.println(DFS(graph));	
		}
	}
	
	public static void createGraph(MyGraph graph){
		int a, b;
			
		//初始化节点数据
		for(int i=0; i<graph.vexnum; i++){
			graph.vexnode[i].data = i;
		}
		
		for(int i=0; i<graph.arcnum; i++){
			//vexnode中的数据是从0开始,所以这儿都-1
			a = sc.nextInt() - 1;
			b = sc.nextInt() - 1;
				
			Edge edge;
			
			//连接a,b两个顶点
			edge = new Edge();
			edge.index = a;
			edge.next = graph.vexnode[b].firstedge;
			graph.vexnode[b].firstedge = edge;
			
			edge = new Edge();
			edge.index = b;
			edge.next = graph.vexnode[a].firstedge;
			graph.vexnode[a].firstedge = edge;
			
		}
	}
	
	//深搜
	public static int DFS(MyGraph graph){
		boolean visited[] = new boolean[graph.vexnum+1];
		int result = 0;
		Stack<Integer> stack = new Stack<Integer>();
		
		for(int i=0; i<graph.vexnum; i++){
			visited[i] = false;
		}
		stack.push(0);
		visited[0] = true;
		
		while(!stack.empty()){
			int num = stack.pop();
			Edge edge = graph.vexnode[num].firstedge;
			//对从栈中取出的数据搜索
			while(edge != null){
				int index = graph.vexnode[edge.index].data;
				//如果没有访问过
				if(visited[index] == false){
					visited[index] = true;
					result ++;
					//入栈
					stack.push(index);
				}
				edge = edge.next;
			}
		}
		
		return result;
	}
	/*
	public static void print(MyGraph graph){
		for(int i=0; i<graph.vexnum; i++){
			System.out.println(i);
			Edge edge = graph.vexnode[i].firstedge;
			while(edge != null){
				System.out.print(graph.vexnode[edge.index].data+1);
				edge = edge.next;
			}
		}
	}
	*/
}



三、第三种做法:数组
可以直接用数组做出来:
1.建一个二维数组,如果认识则置为1,否则为0
2.首先将跟 1 一组的入栈
3.然后仿照深搜,计算得到结果。

package com.ct.graph;

import java.util.Scanner;
import java.util.Stack;

/** 
 * @author MoonMonster
 * @date 2015-10-16 下午03:52:27  
 */
public class Test {
	static Scanner sc = new Scanner(System.in);

	public static void main(String[] args) {
		int n, m;
		int T;
	
		T = sc.nextInt();
		while(true){
			if(T == 0){
				break;
			}
			T --;
			
			n = sc.nextInt();
			m = sc.nextInt();
			
			System.out.println(count(n,m));
		}
	}
	
	public static int count(int n, int m){
		int result = 0;
		Stack<Integer> stack = new Stack<Integer>();
		
		//因为是从1开始的,所以数组要为n+1
		int[][] num = new int[n+1][n+1];
		//判断该数是否已经加过
		boolean visited[] = new boolean[n+1];
		//初始化
		for(int i=0; i<n+1; i++){
			visited[i] = false;
		}
		
		//构建数组
		for(int i=0; i<m; i++){
			int a = sc.nextInt();
			int b = sc.nextInt();
			num[a][b] = 1;
			num[b][a] = 1;
		}
		
		//将跟'1'一组的数据压入栈
		for(int i=0; i<n; i++){
			if(num[1][i] != 0){
				stack.push(i);
			}
		}
		
		while(!stack.empty()){
			final int s = stack.pop();
			//如果该数没被访问过
			if(visited[s] == false){
				result ++;
				visited[s] = true;
				//获得跟s一组的数
				for(int i=0; i<n+1; i++){
					//如果跟s一组且没有被访问
					if(visited[i]==false && num[s][i]==1){
						stack.push(i);
					}
				}
			}
		}
		
		return result - 1;
	}
}


因为链接失效了,所以没法判断第二种和第三种做法的代码是否能得到正确结果(题目给出的测试数据通过了),但第一种肯定是通过了的。



深知自己能力之不足,所以若有谁偶然看到这篇博文,发现错误或有待提高之处,先谢谢各位的纠正,指出。

你可能感兴趣的:(多种方法解决”图+深搜“编程题目)