几种常见的枚举方式:for嵌套,二进制压缩,DFS

就算是五一也不要停下来啊!(一个没买到票的蒟蒻的怒吼)在这里插入图片描述
“99%的题都能靠枚举做出来的,限制枚举算法的是时间与空间。”

方法一:for循环枚举
优点:书写方便
缺点:遇到枚举数量较多或枚举数量不确定时难以应用,而且这种枚举方式看起来不优雅
例题 :扶老奶奶过马路
一共有5个红领巾,编号分别为A、B、C、D、E,老奶奶被他们其中一个扶过了马路。

五个红领巾各自说话:

A:我和E都没有扶老奶奶

B:老奶奶是被C和E其中一个扶过大街的

C:老奶奶是被我和D其中一个扶过大街的

D:B和C都没有扶老奶奶过街

E:我没有扶老奶奶

已知五个红领巾中有且只有2个人说的是真话,请问是谁扶这老奶奶过了街?

【输入形式】

无输入。

【输出形式】

若有多个答案,在一行中输出,编号之间用空格隔开。

例如:A B C D E(这显然不是正确答案)

#include<iostream>
using namespace std;
int main()
{
	for(int i=1;i<=5;i++)
	{
		int	a=0;
		if(i!=1&&i!=5)a++;
		if(i==3||i==5)a++;
		if(i==3||i==4)a++;
		if(i!=2&&i!=3)a++;
		if(i!=5)a++;
		if(a==2)
		{
			char ch=i-1+'A';
			cout<<ch<<" ";
		}
	}	
} 

方法二:二进制压缩
优点:适用于只判定两种情况(选或不选,真或假)或枚举数量不确定的情况,也可以拓展出三进制压缩,四进制压缩来判定多种情况

例题:
ABCDE中num个人做了好事,truth个人说真话。

A说:“我和X中有且只有一个做了好事”

B说:“C和E中有人做了好事”

C说:“我和D和A中有人做了好事”

D说:“B和C都没有做好事”

E说:“我没有做好事”

请问有哪些人做了好事?

多组方案请一行一个输出。

【输入形式】

仅一行,先是一个整数num,接着是一个A~E的字符X,最后是一个整数truth。意义见题面。

【输出形式】

每行输出一组方案,方案中按 字母序 输出做好事的人的代号(A~E),不要空格隔开。

如果没有合法方案,输出一行一个0即可。

【样例输入】

1 E 2

【样例输出】

0

【样例输入】

2 E 2

【样例输出】

BD

BE

通过二进制压缩嵌套枚举说真话的人和做好事的人是哪些

例如二进制数10101代表A C E做了好事,二进制数00111代表CDE说了真话
要枚举所有情况的话我们只需要从二进制数000000枚举到111111即可
000000转换为十进制是0
111111转换为十进制是2^5-1=31
所以我们只需要用一个循环

for(int op=0;op<pow(2,n);op++)

n代表枚举数量,这道题n是5;
接下来可以把这个数转化为二进制数,转换进制的方法想必各位都知道就不累述了。但这里要讲另一个方法

for (int op = 0; op < pow(2, n); op++) {
		for (int j = 0; j < 5; j++) {
			if (op >> j & 1) {
				cout<<"1";
			}
			else cout<<"0";
		}
		cout<<endl;
	}

位运算这鬼东西我至今没搞懂,建议直接背过
当op>>j&1的值为true时,表示op的二进制下第j位上的数是1
输出结果

00000
10000
01000
11000
00100
10100
01100
11100
00010
10010
01010
11010
00110
10110
01110
11110
00001
10001
01001
11001
00101
10101
01101
11101
00011
10011
01011
11011
00111
10111
01111
11111

这题的题解代码就暂时不给出来了,自己动手做出来知识才是自己的在这里插入图片描述


可能有同学看到这里的时候已经是昏昏欲睡了在这里插入图片描述

你还有一个算法没有学,现在还不能休息哦

方法三:dfs枚举(深度优先搜索)
典中典的枚举方式,深搜的经典入门应用场景
都1202年了,不会有人连dfs都不会吧(滑稽保命)
在这里插入图片描述

例题:
给定一个整数 n,将数字 1∼n 排成一排,将会有很多种排列方法。

现在,请你按照字典序将所有的排列方法输出。

输入格式
共一行,包含一个整数 n。

输出格式
按字典序输出所有排列方案,每个方案占一行。

数据范围
1≤n≤7
输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

用next_permutation可以秒切
空位有三个,设数列为 0 0 0(0代表没有填写)
首先填写第一个空,填写好后数列为1 0 0
再填写第二个,可以选2或3,我们先选填2,现在是1 2 0
再填第三个,只有3可以选 所以是1 2 3
这是一种情况,输出
现在回退一位,变成1 2 0
再填写3会重复,第三位已经没有其他填写方案了,所以再回退一位,变成1 0 0
这时看第二位,之前填写过2了,所以这次可以填写3
数列变成了1 3 0
再看第三位,1和3被用过了,只剩下2,所以填写结果是 1 3 2
同理,回退两次变回1 0 0
由于 1 2 3和1 3 2都被输出过了,以1为第一位以及没有其他方案了,所以再回退一次,变成0 0 0
这次第一位填2变成 2 0 0
第二位填1 变成 2 1 0
第三位填 3 变成 2 1 3
这又是一种情况,输出
…………

#include <iostream>
using namespace std;
const int N = 10;
int path[N];//储存数据
bool st[N];//判断一个数是否已经被用过了
int n;

void dfs(int u) { //u代表现在已经填写的数量
	if (u == n) { //如果填写数量已经达到了n的限制,就把数组输出出来
		for (int i = 0; i < n; i++)
			printf("%d ", path[i]);
		puts("");
		return;
	} else {
		for (int i = 1; i <= n; i++) {
			if (!st[i]) { //如果i这个数没被前面的数用过,就放进去
				path[u] = i;
				st[i] = true;//现在i这个数已经被用了,在st数组中标记一下
				dfs(u + 1); //递归下一个空
				path[u] = 0;//恢复现场
				st[i] = false;//这个位置的i被清除了,i又回到没有被用的状态
			}
		}
	}
}

int main() {
	scanf("%d", &n);
	dfs(0);
	return 0;
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值