蓝桥杯 历届试题 分考场 java实现 dfs 回溯

问题描述

  n个人参加某项特殊考试。
  为了公平,要求任何两个认识的人不能分在同一个考场。
  求是少需要分几个考场才能满足条件。

输入格式

  第一行,一个整数n(1<n<100),表示参加考试的人数。
  第二行,一个整数m,表示接下来有m行数据
  以下m行每行的格式为:两个整数a,b,用空格分开 (1<=a,b<=n) 表示第a个人与第b个人认识。

输出格式

  一行一个整数,表示最少分几个考场。

样例输入

5
8
1 2
1 3
1 4
2 3
2 4
2 5
3 4
4 5

样例输出

4

样例输入

5
10
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
4 5

样例输出

5

解析:

首先,分析一下,这道题,认识关系就相当于两个点相邻,而相邻就不同考场,即不同色,嘿嘿,想到了地图涂色问题,由此

这道题的方法就是回溯+剪枝。

其次,n的数量级,不超过100,显然,完全可以暴力,于是乎我采用了比较好理解的方法,dfs中套了两层for循环(),时间复杂度O(n^3),不怂。

嗯嗯,话不多说,看代码



import java.io.IOException;
import java.util.Arrays;
import java.util.Scanner;
//分考场
public class Question5 {

	final static int maxn=100;
	final static int INF=0X3f3f3f3f;
	static int ans=INF;//答案
	static boolean flag=false;//某人是否可以进入某考场,false表示不可以
	static int que[][]=new int[maxn+5][maxn+5];//某考场对应的位置的人
	static boolean vis[][]=new boolean[maxn+5][maxn+5];//两个人之间的认识关系,若认识,则为true;
	static int queMember[]=new int [maxn+5];//某考场人的数量
	static int num=0;//考场数
	static int n,m,a,b;
	
	static void dfs(int p,int num)//p 人,从1开始  num:考场数
	{
		if(ans<=num)//剪枝
		{
			return ;
		}
		if(p>n)
		{
			if(ans>num)
				ans=num;
			return;
		}
		for(int j=1;j<=num;j++)
		{
			flag=true;//初始可以进入考场
			for(int k=1;k<=queMember[j];k++)
			{
				if(vis[p][que[j][k]])//若不相容
				{
					flag=false;
					break;
				}
			}
			if(flag)//若相容
			{
				queMember[j]++;
				que[j][queMember[j]]=p;
				dfs(p+1,num);
				queMember[j]--;//回溯  
				flag=false;//如果之前所有考场都不可以安排,则选择新建一个考场  如果没有这句可能不会新建考场
			}
		}
		if(!flag)//如果之前的考场都无法与p相容,那么p新建一个考场
		{
			num++;
			queMember[num]=1;
			que[num][1]=p;
			dfs(p+1,num);//新建考场之后不用回溯,因为已经没有其他可能,需要回溯的只会出现在有几个已建考场都容纳p时,选择一种最优的
			//eg  5 4    1 3 2 4 2 5 3 4  最优应该为2个考场 即1,2不在一个考场。
		}
		
	}
	public static void main(String[] args) throws IOException {
		Scanner cin=new Scanner(System.in);
		n=cin.nextInt();
		m=cin.nextInt();
		for(int i=1;i<=n;i++)
		{
			Arrays.fill(vis[i], false);
			Arrays.fill(que[i],-1);
			
		}
		for(int i=0;i<m;i++)
		{
			a=cin.nextInt();
			b=cin.nextInt();
			vis[a][b]=true;
			vis[b][a]=true;
		}
		dfs(1,0);
		System.out.println(ans);
		cin.close();
	}

}

检测:

详细记录
评测点序号 评测结果 得分 CPU使用 内存使用 下载评测数据
1 正确 20.00 218ms 22.96MB 输入 输出
2 正确 20.00 234ms 22.26MB VIP特权
3 正确 20.00 421ms 26.10MB VIP特权
4 正确 20.00 359ms 25.19MB VIP特权
5 正确 20.00 234ms 21.15MB VIP特权

感受:

最主要是理清思路,可以先写出伪代码,然后在根据其写出Demo,不要直接就开始写,不然的话可能会考虑不周全,就像我这次,第一次直接写了个3层for循环,没有回溯,就导致忽略了有多个考场可以容纳p,选择第一个可以容纳p的考场,可能并不是最优的选择。主要是思路,一定要考虑周全,不能太慌!

猜你喜欢

转载自blog.csdn.net/Look_star/article/details/88133430