如【图1.jpg】, 有12张连在一起的12生肖的邮票。
现在你要从中剪下 5 张来,要求必须是连着的。
(仅仅连接一个角不算相连) 比如,【图2.jpg】,【图3.jpg】中,粉红色所示部分就是合格的剪取。
请你计算,一共有多少种不同的剪取方法。
思考:
思路:暴力求全排列+检查连通性。
(1)用递归暴力列出所有可能的排列:从12个数中选5个数进行全排列。
(2)判断这5个数是否连通。
算法复杂度:12*11*10*9*8=121/7=95040
可行!
判断2个数是否连通
小技巧:
·在原图中向上为-4,向下为+4,向左为-1,向右为+1,但是遇到34578这种4+1=5,这种情况不符合,所以重构一下原图。
·向上为-5,向下为+5,向左为-1,向右为+1。
判断图中的{2,3,4,8,9}是否连通。用队列:
•2进队列:当前队列是(2);
•2的邻居进(push)队列:当前队列是(2 3);
•弹出(pop)2:当前队列是(3);
•3的邻居进队列:当前队列是(348);
•弹出3;当前队列是(48)
•4的邻居进队列:当前队列是(489)
•弹出4:当前队列是(89)
•8没有没处理过的邻居了。·弹出8:当前队列是(9)·弹出9;
•队列空。
如果5个数都进过队列,它们就是连通的。
参考代码:
from queue import *
def bfs(): #a[0]~a[4]这前5个数是递归出的5个数,用BFS判断是否连通
vis=[0]*5 #用来标记5个数的状态,判断其中某个数是否已经用队列处理过
p=0 #进队列的个数,如果5个数都进过队列,说明这五个数连通
q=Queue() #创建队列
q.put(0) #进队列
vis[0]=1 #标记,表示 0 被队列处理过了
while not q.empty(): #是否为空
i=q.get() #得到队列的第一个数
p+=1
for j in range(5):
if vis[j]==0: #j 没有用队列处理过
for k in (-1,1,-5,5): #k 是上下左右四个方向
if a[i]+k==a[j]: #与 j 在 k 方向上连接
q.put(j)
vis[j]=1 #进队列了
if p==5:
return True
else:
return False
def perm(s,t):
global num
if s==5: #得到一个 5 个数的全排列,用bfs判断 5 个数是否连通
if bfs()==True:
num+=1
else:
for i in range(s,t+1):
a[s],a[i]=a[i],a[s] #交换
perm(s+1,t)
a[i],a[s]=a[s],a[i]
a=[1,2,3,4,6,7,8,9,11,12,13,14]
num=0 #统计排列数
perm(0,11) #求从第 0 个数到第 11 个数的全排列
print(num//120)
#print(116) 最终答案
排列转化为组合num//5x4x3x2x1,分母是选的某几个数,5个数就是5的阶乘,4个数就是4个数的阶乘