#蓝桥#JAVA#分考场
题目描述
n个人参加某项特殊考试。
为了公平,要求任何两个认识的人不能分在同一个考场。
求是少需要分几个考场才能满足条件。
输入描述
输入格式:
第一行,一个整数n(1≤n≤100),表示参加考试的人数。
第二行,一个整数m,表示接下来有m行数据。
以下m行每行的格式为:两个整数a,b,用空格分开 (1≤a,b≤n)表示第a个人与第b个人认识。
输出描述
输出一行一个整数,表示最少分几个考场。
问题分析
本题的核心问题是在给定一定数量的学生以及学生之间的认识关系的情况下,找出使用最少考场来安排所有学生的方案,并且要保证每个考场内的学生彼此都不认识。由于需要探索所有可能的考场安排组合,从中找出最优解,所以可以采用深度优先搜索(DFS)算法来解决。
1. 穷举所有可能的方案
在这个问题中,每个学生都有多种选择,可以被安排到已有的某个考场,或者新开一个考场。因为学生数量和考场安排的可能性众多,要找出使用最少考场的方案,就需要遍历所有可能的安排方式。DFS 算法的特点是能够按照一定的顺序,递归地深入搜索每一种可能的路径,从而实现对所有可能方案的穷举。 例如,对于第一个学生,它可以被安排到第一个考场;对于第二个学生,它可以选择和第一个学生在同一个考场(前提是他们不认识),也可以新开一个考场。通过 DFS,我们可以依次尝试所有这些可能性,确保不会遗漏任何一种安排方案。
2. 回溯机制
便于尝试不同选择 DFS 算法具有回溯机制,这在本题中非常有用。当我们将一个学生安排到某个考场后,继续递归安排后续学生。如果后续发现这种安排方式无法满足所有学生的安排或者不是最优方案,我们可以通过回溯操作撤销之前的安排,尝试其他的安排方式。 在代码中,当我们将学生 `x` 安排到 `nums[i][j]` 座位后,递归调用 `dfs` 方法安排下一个学生。如果后续的递归过程中发现这种安排不行,返回后会将 `nums[i][j]` 置为 0,即撤销该安排,然后尝试将学生 `x` 安排到其他考场或座位。
3. 剪枝优化提高效率
在搜索过程中,我们可以利用剪枝策略来减少不必要的搜索。在本题中,如果当前使用的考场数量已经大于等于之前记录的最少考场数量,那么继续搜索下去不可能得到更优的方案,此时可以直接返回,避免继续深入搜索,从而提高算法的效率。 例如,在 `dfs` 方法中,当 `k >= sum` 时,就直接返回,不再进行后续的搜索。
4. 递归实现
简单直观 DFS 算法通常使用递归的方式实现,代码结构清晰,逻辑简单直观。在本题中,通过递归调用 `dfs` 方法,不断地安排下一个学生,同时更新考场数量,使得代码的实现和问题的解决思路高度一致,易于理解和维护。 综上所述,由于本题需要穷举所有可能的考场安排方案,并且需要回溯机制来尝试不同的选择,同时可以利用剪枝优化提高效率,因此使用 DFS 算法是一个合适的选择。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;public class Main {static int sum = 110;// 最多可以安排的教室static int nums[][] = new int [100][100]; //nums[i][k] 考场表 i 表示考场,k表示座位 static boolean visi[][] = new boolean [100][100];// 记录考场信息static int n; // 学生数量static int m; // 关系数据的数量static BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));public static void main(String[] args) throws IOException{String [] sc = bf.readLine().split(" ");n = Integer.parseInt(sc[0]);sc = bf.readLine().split(" ");m = Integer.parseInt(sc[0]);for (int i = 0; i < m ; i++){sc = bf.readLine().split(" ");int x = Integer.parseInt(sc[0]);int y = Integer.parseInt(sc[1]);// 标记互相认识的人visi[x][y] = true;visi[y][x] = true;}dfs(1, 1);System.out.println(sum);}// x 代表第几个学生, k考场public static void dfs(int x,int k){// 剪枝 如果当前考场大于最小考场,就没必要继续则返回if(k >= sum) return;// 第x学生>全部学生,表明全部的学生都进入了考场,所有学生都有考场if(x > n){sum = Math.min(k, sum);//只需从当前考场值和当前最小值中取最小值return;}//为每一位学生分配考场for (int i = 1; i <= k; i++){int j = 0;//考场有人且无认识的人//i代表考场 j 代表座位号 while(nums[i][j] != 0 && !visi[x][nums[i][j]]){j++;}//考场为空if(nums[i][j] == 0){// x 进入nums[i][j]考场nums[i][j] = x;//下一位同学进行分配dfs(x + 1, k);//回溯 以便找到更好的方法nums[i][j] = 0;}}// 开辟完所有的考场发现都认识那就只可以再开一所考场了nums[k + 1][0] = x;dfs(x + 1, k + 1);//回溯nums[k + 1][0] = 0;}
}