穷举,我真的会谢

115 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第23天,点击查看活动详情

枚举

在学校算法时候,我们最常提到的就是暴力法,那什么是暴力算法?

枚举算法,又叫穷举算法,听起来就是特别的暴力,那么我们从一道乘法题入手吧。

?3 * 6528=3?*8256 请在两个问号处填入相同数字,使得等式成立。

那么这题太简单了,三行代码就能完成了。

for(int i=0;i<=9;i++){
if((i*10+3)*6258==(30+i)*8256){
System.out.println(i);
}
}

从代码可以看出来,枚举就是有序的尝试每一种可能。那么问题再次深入,如果 ???+???=??? 把数字1-9填入?,每个数字只能使用一次让等式成立,例如173+286=459,那么一共有多少种组合呢?173+286=459和286+173=459算一种。

这样情况下,我们最好还是使用代码完成(不过手算也可以,比较耗费时间罢了)

代码部分:我们需要定义九个变量,用来代表1-9,然后对每一个数进行循环,判断是不是相等。 比如 abc标识第一个加数的个位十位百位,def表示第二个数的个位十位百位,ghi表示和的个位十位百位。判断: 如果a不等于bcdefghi,b不等于cdefghi,c不等于defghi....且a100+b10+c+d100+e10+f=g100+h10+i 这样判断之后,得到的结果除以2就是满足我们条件的结果。

image.png

看到这么多次循环,人已经麻了,感觉脑子晕晕的,能不能有简单一点的呢?道理我都懂,可是O(9^9)近似O(n^n)时间复杂度也太大了吧。好变态。

不知道大家还记不记得桶排序呢?我们这个地方是不是可以用桶排序的标记方法,选择了他就把他标记一下,下次就不能选了,并且可以直接用一个数组来存储变量。

这里我们定义一个book数组,长度为10,从book[1]到book[9]代表a-i的数字。

image.png

好家伙,这就叫刚出虎穴又入狼窝吧,我以为你简化了,结果感觉比第一种方法还复杂,禁止套娃,给你一分钟,快速给我解决,不然......

马上就来简单的,深度优先搜索。

首先通过一个例子,了解深度优先算法。

假设我们有123三张牌,并且有123编号的三个盒子,保证每个盒子只有一张牌,一共有几种放法?

这个简单 123,132,213,231,312,321 六种。

啊这,我能怎么说。每个盒子对应都有三种可能,那么假设1给了1号盒子,到2号盒子时候,只有两张牌了,所以把2放进去,来到三,你会突然发现,自己只有一张牌了(感觉很废话耶),当把第三张放进去,就得到了一种结果,那么到这里是不是结束了?当然没有。

产生了一种可能后,需要立即后退,看看有没有新的可能,那么3回退到2,手里两张牌,把另外一张放进去...... 同理退回到1。

说了这么多,那么怎么用程序实现呢??? 解决第一个问题,怎么放牌,每个盒子都可能有1 2 3,那么可以使用循环做:

for(int i=1;i<=3;i++){
a[step]=i;//把第i张牌,放到第step牌里面
}

如果当前牌已经使用过了就需要标记,我们引入book数组。book【i】=1,表示这个牌不在手上了。处理完step,那么step+1如何处理?和step类似。

我们可以封装一个dfs函数:

image.png

这样我们可以收获一个dfs模板

void dfs(int step){
判断边界;
尝试可能 for(int i=1;i<=n;i++){
继续下一步 dfs(step+1);
}
返回
}

那么对应我们九个数字,我们是不是也可以选择这种方式?

边界:if(a[1]*100+a[2]*10+a[3]+a[4]*100+a[5]*10+a[6]==a[7]*100+a[8]*10+a[9]){total++; return}

可能:for(int i=1;i<=9;i++){}

下一步:dfs(step++)

dfs代码如下:

int a[10],book[10],total=0;
void dfs(int step){
if(step==10){
if(a[1]*100+a[2]*10+a[3]+a[4]*100+a[5]*10+a[6]==a[7]*100+a[8]*10+a[9]){
total++; 
return;
}
for(int i=1;i<=9;i++){
if(book[i]==0){
a[step]=i;
book[i]=1;
dfs(step+1);
book[i]=0;
}
}
return
}
}