问题描述
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();
}
}
检测:
详细记录 |
|
感受:
最主要是理清思路,可以先写出伪代码,然后在根据其写出Demo,不要直接就开始写,不然的话可能会考虑不周全,就像我这次,第一次直接写了个3层for循环,没有回溯,就导致忽略了有多个考场可以容纳p,选择第一个可以容纳p的考场,可能并不是最优的选择。主要是思路,一定要考虑周全,不能太慌!