注意, n , t ⩽ 10 n,t \leqslant 10 n,t⩽10,这就表明这题可以直接用搜索。
第一步:dfs中需要哪些参数
必不可免的两个:前
x
x
x 人分成了
t
t
t 组。
那么怎么记录这些组呢?n很小,t也很小,那么可以让一个数的第
i
i
i 位代表第
i
i
i 个人在那一组,即状态压缩。
第二步:考虑剪枝
首先考虑一定完不成的是什么情况,就是剩下的人数
<
<
< 还需要的组数,这样的话一定凑不满
t
t
t 组。
还有如果当前组数与要求组数相同就不新建小组了,因为多建小组没有任何意义,不可能是答案中的一种。
第三部:实现
实现很简单,考虑两种情况
- 新建小组,那么与它相斥的人不能在这一组中(注意回溯)
- 加入原来的小组,要满足第 x x x 人与原来的小组内的每一人不相斥
记录相斥:
for (int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v;
G[u].push_back(v), G[v].push_back(u);
}
记录与第 x x x 人相斥的人不能在 第 i i i 组:
if (t - tm <= n - x && tm)
for (int i = 1; i <= tm; i++)
{
if (a[i][x]) continue;
for (int j = 0; j < G[x].size(); j++) a[i][G[x][j]]++;
num = num * 10 + (i - 1);
dfs(x + 1, tm, num);
for (int j = 0; j < G[x].size(); j++) a[i][G[x][j]]--;
num /= 10;
}
那么最终代码就得出来了:
#include <iostream>
#include <map>
#include <vector>
using namespace std;
const int NR = 20;
vector<int> G[NR];
int n, t, m, a[NR][NR], ans;
map<long long, bool> vis;
void dfs(int x, int tm, long long num)
{
if (x == n + 1 && tm == t && !vis[num])
{
vis[num] = true, ans ++;
return;
}
else if (x == n + 1) return;
if (t - tm <= n - x && tm)
for (int i = 1; i <= tm; i++)
{
if (a[i][x]) continue;
for (int j = 0; j < G[x].size(); j++) a[i][G[x][j]]++;
num = num * 10 + (i - 1);
dfs(x + 1, tm, num);
for (int j = 0; j < G[x].size(); j++) a[i][G[x][j]]--;
num /= 10;
}
if (tm == t) return;
tm++;
for (int i = 0; i < G[x].size(); i++) a[tm][G[x][i]]++;
num = num * 10 + (tm - 1);
dfs(x + 1, tm, num);
for (int i = 0; i < G[x].size(); i++) a[tm][G[x][i]]--;
num /= 10;
}
int main()
{
cin >> n >> t >> m;
for (int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v;
G[u].push_back(v), G[v].push_back(u);
}
dfs(1, 0, 0);
cout << ans << '\n';
return 0;
}