题目描述:
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
核心思想:
类似于图着色问题。
回溯算法:
利用递归,暴力枚举每一种分配方案。如果当前方案可能得出的解已经不可能优于最优解,则及时返回(即剪枝)。
细节见代码。
代码如下:
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int N=110;
int a[N];
vector<int>mp[N];
int ans=100;
void dfs(int k,int n)
{
int mx=0;
for(int i=1;i<k;i++)
mx=max(mx,a[i]);
if(mx>=ans)//mx不优于当前最优解,剪枝
return;
if(k>n)//所有人安排完毕
{
ans=min(ans,mx);
return;
}
for(int i=1;i<=mx;i++)//依次判断第k个人能否放在i考场中
{
int l=mp[k].size(),flag=0;
for(int j=0;j<l;j++)//判断第i个考场中是否有k的熟人
{
if(a[mp[k][j]]==i)//k的第j个熟人在考场i中,则k不能放在第i个考场中
{
flag=1;
break;
}
}
if(!flag)//第i个考场中没有k的熟人,k可以放在第i个考场中,递归此方案
{
a[k]=i;
dfs(k+1,n);
}
}
a[k]=mx+1;//第k个人放到全新的一个考场
dfs(k+1,n);
return;
}
int main()
{
int n,m,x,y;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
//回溯的时候,例如,3认识7,给3分配考场的时候,7还没有分配,
//但是在前面递归的时候给7分配了,导致3躲避了上次考试的7。
//由于递归的原因,小的认识大成为了不必要的干扰项
//解决方法就是,将无向图存为大认识小,而小不认识大的有向图,
//分配的时候先分配小的,大的还未分配,不需要考虑避嫌大的
if(x>y)
mp[x].push_back(y);
else if(x<y)
mp[y].push_back(x);
}
dfs(1,n);
printf("%d\n",ans);
return 0;
}