【题解】AT_abc289_c 题解
AT_abc289_c 题解
思路分析
一道基础的深搜模板题,建议参考:P1157 组合的输出。
首先,我们需要先进行选择。由于选择的集合之间没有顺序之分,所以采用组合选取的方式。
那么如何进行组合选取呢?我们每次只需要选择比前一个选择的集合的编号大的集合(严格大于)就可以了。这样子的定序枚举就可以保证是组合选取(因为没有顺序之分你就要给他规定一个顺序),接下来就可直接使用深搜解决。
那么进行完组合选取之后又该怎么办呢?就要进行判断。题目要求所选出的集合中从 到 都得至少有一个,于是可以使用类似桶排序的思想。我们开一个数组记录 到 记录是否出现过。出现过就记录,最后判断是否都出现就可以了。
按照上述步骤进行实现即可,注意:要选择的集合个数不确定,要逐个枚举选择要选择的集合个数并进行搜索。
代码
#include <iostream>
using namespace std;
int n, m, a[15][15], t[15], c[15], cnt; //如题意,c 为每个集合的元素个数
bool used[15], pded[15]; //前者为使用数组,后者为判断数组
void dfs(int r, int pos)
{
if(pos > r) //选了 r 个数(r为选择的要选择的集合个数)
{
memset(pded, false, sizeof(pded)); //重置判断数组。
for(int i = 1;i <= r;i++)
{
for(int j = 1;j <= c[t[i]];j++) //遍历每个选择的集合
{
pded[a[t[i]][j]] = true; //记录该数出现过
}
}
bool pd = pded[1]; //判断 1,为后面的与操作做准备
for(int i = 2;i <= n;i++)
{
pd = pd && pded[i]; //与操作,1 到 n 都要出现。
}
cnt += int(pd); //记录个数
return;
}
for(int i = 1;i <= m;i++) //拓展
{
if(!used[i] && i > t[pos - 1]) // 如果没选择过而且满足顺序
{
t[pos] = i; //选择
used[i] = true; //记录被选择
dfs(r, pos + 1); //进行下一步选择
used[i] = false; //回溯
}
}
}
int main()
{
cin >> n >> m;
for(int i = 1;i <= m;i++)
{
int q;
cin >> q;
c[i] = q;
for(int j = 1;j <= q;j++)
{
cin >> a[i][j];
}
}
for(int i = 1;i <= m;i++) dfs(i, 1); //逐个枚举选择要选择的集合个数并进行搜索
cout << cnt << endl;
return 0;
}

浙公网安备 33010602011771号