资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
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
总结:
开始用贪心,只能符合一部分测试。因为贪心算出来并不总是最优解。用DFS,每次添加人时,不再是仅仅在最后一个考场中进行。而是,同时考虑其在所有考场中的情况,不断DFS判断找到最优解。
代码:
#include<iostream>
#include<vector>
#include<set>
#include <queue>
#include<algorithm>
using namespace std;
typedef long long ll;
int n,m,ans=0x3f3f3f3f;
//不认识,没访问(主要全部初始化为true要用for循环,比较麻烦)
bool G[100][100]={false},inq[100];
//注意!这样初始化,只会第一个为true值!!
//inq[100]={true};
//初始化各个考场,ps:其实用普通数组还好点
vector<int> room[105];
void BFS(int cnt,int id)
{
//最优解筛选(剪枝)
if(cnt>=ans) return;
//安排完所有的人,递归出口
if(id>n)
{
ans= min(ans,cnt);
return;
}
for(int r=1;r<=cnt;r++)
{
bool flag=0;
//罗列r号考场内所有选手,
for(vector<int>::iterator it= room[r].begin();it!=room[r].end();it++)
{
//如果认识考场里的人,跳出
if(G[id][*it]==true ) {flag=1;break;}
}
//所有人都不认识,那就在这个考场中进行DFS
//if(flag==0) room[r].insert(room[r].begin(),id),BFS(cnt,id+1),room[r].erase(room[r].begin());
if(flag==0) room[r].push_back(id),BFS(cnt,id+1),room[r].pop_back(); //回溯,仔细想想,递归到最深处,压入弹出都是最后一个,所以上面的操作没有必要
}
//如果所有房间都有认识的人,那么单独开房。而如果有房间的话,下面的操作必然不是最优解,其实可以加以判断的,算了反正ac了
room[cnt+1].push_back(id);
BFS(cnt+1,id+1);
room[cnt+1].pop_back();
}
int main ()
{
cin>>n>>m;
while(m--)
{
int x,y;
cin>>x>>y;
//建立映射关系
G[x][y]=G[y][x]=true;
}
//第一个考场开始,有归属的人为1
BFS(1,1);
cout<<ans<<endl;
return 0;
}
最后附上 错误的贪心算法,引以为戒!!!
#include <iostream>
#include <vector>
#include <set>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long ll;
int n, m, ans = 0x3f3f3f3f;
//不认识,没访问(主要全部初始化为true要用for循环,比较麻烦)
bool G[100][100] = {false}, inq[100];
//注意!不能这样初始化,这样只会第一个为true值!!
//inq[100]={true};
//初始化各个考场
vector<int> exam[105];
void BFS(int cnt, int num)
{
if (cnt > ans)
return;
//递归出口:有所属的人等于总人数
if (num == n)
{
ans = ans > --cnt ? cnt : ans;
return;
}
for (int r = 1; r < cnt)
for (int u = 1; u <= n; u++)
{
if (inq[u] == true)
continue;
bool flag = 0;
//罗列cnt号考场内所有选手,并添加不认识的人
for (vector<int>::iterator it = exam[cnt].begin(); it != exam[cnt].end(); it++)
{
//如果认识考场里的人,跳出,开始判断下一个人
if (G[u][*it] == true)
{
flag = 1;
break;
}
}
//所有人都不认识,添加此人于该考场中,设置其有归属了,有归属的人数+1
if (flag == 0)
exam[cnt].push_back(u), inq[u] = true, num++;
}
//判断完一个考场,进行下一考场的判断
BFS(++cnt, num);
}
int main()
{
cin >> n >> m;
while (m--)
{
int x, y;
cin >> x >> y;
//认识了
G[x][y] = G[y][x] = true;
}
inq[1] = true, exam[1].push_back(1);
//第一个考场开始,有归属的人为1
BFS(1, 1);
cout < < < < endl;
return 0;
}
参考的文章:
- https://www.cnblogs.com/RioTian/p/13654092.html
- https://www.cnblogs.com/henuliulei/p/10548685.html