蓝桥杯国赛真题【分考场】
题意
思路
由于数据比较小,而且题意中的“认识”仅限于两个人之间,没有传递的意思,我们可以用爆搜来写,具体来看,我们先准备至少n个考场,从第一位考生开始爆搜。对于当前枚举的考生x,我们有两种选择,第一个:我们将当前考生放置已经存在考生的考场y(枚举已经存在考生的考场)中,满足该选择的条件是:考场y中的所有考生和当前枚举的考生均不认识。第二个:我们使用一个没有考生的教室z,将当前考生x放入进考场z。
算法
具体实现的话,我们用爆搜即可,定义函数void dfs(int u, int cnt)
,其中u
是当前枚举的考生,cnt
是用了多少个考场,两种情况对应在代码中寻找即可,这里额外讲一下本题的重点,如果当前使用的考场不小于我们的答案,我们应该立即返回,即在dfs
函数中return
,这是一个很优秀的剪枝技巧,此外,如果本题的剪枝条件为:使用的考场大于我们的答案。会导致本题最后一个样例因为超时而过不了本题,所以我们应当选用尽可能优秀的剪枝。
AC代码(C++)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 110;
int n, m, res = N;
bool re[N][N];
vector<int> rooms[N];
void dfs(int u, int cnt) {
if(cnt >= res) return ;
if(u == n + 1) {
res = cnt;
return ;
}
for(int i = 1; i <= cnt; i ++) { // 枚举当前考场是否可以放入u号考生
bool flag = true;
for(int j = 0; j < rooms[i].size(); j ++) {
if(re[u][rooms[i][j]]) {
flag = false;
break;
}
}
if(flag) {
rooms[i].push_back(u);
dfs(u + 1, cnt);
rooms[i].pop_back();
}
}
rooms[cnt + 1].push_back(u);
dfs(u + 1, cnt + 1);
rooms[cnt + 1].pop_back();
}
int main() {
cin >> n >> m;
for(int i = 1; i <= n; i ++) re[i][i] = true;
while(m --) {
int a, b;
cin >> a >> b;
re[a][b] = re[b][a] = true;
}
dfs(1, 0);
cout << res << "\n";
return 0;
}