【题解】AT_abc289_c 题解

AT_abc289_c 题解

思路分析

一道基础的深搜模板题,建议参考:P1157 组合的输出

首先,我们需要先进行选择。由于选择的集合之间没有顺序之分,所以采用组合选取的方式。

那么如何进行组合选取呢?我们每次只需要选择比前一个选择的集合的编号大的集合(严格大于)就可以了。这样子的定序枚举就可以保证是组合选取(因为没有顺序之分你就要给他规定一个顺序),接下来就可直接使用深搜解决。

那么进行完组合选取之后又该怎么办呢?就要进行判断。题目要求所选出的集合中从 11nn 都得至少有一个,于是可以使用类似桶排序的思想。我们开一个数组记录 11nn 记录是否出现过。出现过就记录,最后判断是否都出现就可以了。

按照上述步骤进行实现即可,注意:要选择的集合个数不确定,要逐个枚举选择要选择的集合个数并进行搜索。

代码

#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;
}
posted @ 2023-02-15 09:05  邻补角-SSA  阅读(11)  评论(0)    收藏  举报  来源