大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺
描述
描述
某列车调度站的铁道联接结构如Figure 1所示
其中,A为入口,B为出口,S为中转盲端。所有铁道均为单轨单向式:列车行驶的方向只能是从A到S,再从S到B;另外,不允许超车。因为车厢可在S中驻留,所以它们从B端驶出的次序,可能与从A端驶入的次序不同。不过S的容量有限,同时驻留的车厢不得超过m节。
设某列车由编号依次为{1, 2, …, n}的n节车厢组成。调度员希望知道,按照以上交通规则,这些车厢能否以{a1, a2, …, an}的次序,重新排列后从B端驶出。如果可行,应该以怎样的次序操作?
输入
共两行。
第一行为两个整数n,m。
第二行为以空格分隔的n个整数,保证为{1, 2, …, n}的一个排列,表示待判断可行性的驶出序列{a1,a2,…,an}。
输出
若驶出序列可行,则输出操作序列,其中push表示车厢从A进入S,pop表示车厢从S进入B,每个操作占一行。
若不可行,则输出No。
样例
Example 1
Input
5 2
1 2 3 5 4
Output
push
pop
push
pop
push
pop
push
push
pop
pop
Example 2
Input
5 5
3 1 2 4 5
Output
No
限制
1 ≤ n ≤ 1,600,000
0 ≤ m ≤ 1,600,000
时间:2 sec
空间:256 MB
题目分解
这个问题可以分为几步:step1:求入栈序列的全排列 ;step2:根据出栈规则求合法的出栈序列;step3:若S的容量小于A,进行再一步的筛选 step4:输出序列 step5:输出操作
1.全排列
详细描述在这
https://blog.csdn.net/m0_38008539/article/details/95178298
提供两种方法
def perm_byOrder(stack_in,begin,end):
""" 求全排列 -> 调换顺序的方法 :param lis: 需要求全排列的序列 :param begin: 移动变量 :param end: 固定变量 :return: 全排列 """
if begin >= end: #结束标志
stack_perm.append(copy.copy(stack_in))
# print(stack_in)
else:
i = begin
for num in range(begin,end):
stack_in[num],stack_in[i] = stack_in[i],stack_in[num] #固定当前位置,在进行下一位的排列
perm_byOrder(stack_in,begin+1,end)
# 调用结束之后还需要回溯将交换位置的元素还原,以供其他下降路径使用(二叉树)
stack_in[num],stack_in[i] = stack_in[i],stack_in[num]
def perm_bySlice(stack_in):
""" 全排列 -> 利用切片的方法 :param stack_in: 需要求全排列的序列 :return: 全排列 """
if len(stack_in) == 1: #结束条件
return [stack_in]
reslut = []
for i in range(len(stack_in)):
s = stack_in[:i] + stack_in[i+1:] #stack_in[i] 不仅如此下一次递归
p = perm_bySlice(s)
for x in p:
reslut.append(stack_in[i:i+1] + x)
return reslut
2.判断合法输出序列
也有两种方法
- 方法1
将入栈队列和出栈队列进行比较,引入辅助堆栈,将入栈数据和出栈队列进行比较,不相等则继续压栈,相等则辅助堆栈弹出,最后若辅助堆栈为空,证明序列合法
class stack():
def __init__(self, length):
self.length = length
self.top = -1
self.stack = []
def noEmpty(self): #判断不为空
if self.top == -1:
return False
else:
return True
def push(self,data):
if self.top >= self.length:
print('堆栈已满\n')
else:
self.top += 1
self.stack.append(data)
def top_value(self):
return self.stack[self.top]
def pop(self):
if self.noEmpty() is False:
print('堆栈是空的')
else:
self.stack.pop(self.top)
# print('弹出的数据为:%d' % (self.stack.pop(self.top)))
self.top -= 1
def judge_stack_byCompare(stack_out):
""" 通过入栈序列和出栈序列的比较 :param stack_out: 要判断的序列 :return: True or False """
stack_in = stack(len(stack_out))
j = 0
for i in range(len(stack_out)):
stack_in.push(i + 1)
while stack_in.noEmpty() and stack_out[j] == stack_in.top_value():
stack_in.pop()
j = j + 1
if stack_in.noEmpty():
return False #出栈序列不合法
else:
return True #出栈序列合法
- 方法2
通过观察的方法以及出栈的规则,对于序列中的任意一个数其后面所有比他小的数应该是倒序的
def judge_stack_byOrder(stack_out):
""" 对于序列中的任意一个数其后面所有比他小的数应该是倒序的 :param stack_out: 要判断的序列 :return: True or False """
flag = 1
b = [None]
for i in range(len(stack_out)):
k = 0
for j in range(i+1,len(stack_out)):
if stack_out[i] > stack_out[j]: #在比data[i]的范围内讨论
if k == 0: #相当于记录一个基准值,再和其他小于data[j]的数讨论
b[k] = stack_out[j] #记录比data[i]更小的数
k += 1
else:
if stack_out[j] > b[0]:#出现了比记录值还大的数,更改标志
flag = 0
else:#记录更小的数
b[0] = stack_out[j]
return flag
3.S容量小于A的情况,输出合法出栈序列
根据观察的方法,设S的容量为m,A序列为n 针对2中合法的序列,若其中某一元素后有至少m个小于它的,则该序列在此情况下不合法,即排除
def judge_for_m(stack_out):
""" 中转盲站的容量有限,最多能容纳的节数可能会小于输入数据的节数,因此需要在 合法出栈的队列的基础上,在进一步判断 ->【对于序列中的某一个数,其后有两个数小于它,则排除】 :param stack_out: 需要进行判断的出栈队列 :return: """
flag = 1
for i in range(len(stack_out)):
k = 0
for j in range(i+1,len(stack_out)):
if stack_out[i] > stack_out[j]: #找到后面比data[i]小的数
k += 1 #计数
if k >= m:
flag = 0
return flag
4.输出操作
def stack_correct(stacks,n,m):
""" :param stacks: 全排列 :return: 对应m的条件下的合法出栈序列 """
stack_correct= [] #m>=n时的合法序列
stack_wrong = [] #m>=n时的非法序列
stack_correct_advance = [] #m<n时的合法序列
stack_wrong_advance = [] #m<n时的非法序列
for data in stacks:
# if judge_stack_byOrder(data):
if judge_stack_byCompare(data):
stack_correct.append(data)
else:
stack_wrong.append(data)
# 在合法的出栈序列的基础上进一步筛选
if m < n:
for data in stack_correct:
if judge_for_m(data):
stack_correct_advance.append(data)
else:
stack_wrong_advance.append(data)
return stack_correct,stack_wrong,stack_correct_advance,stack_wrong_advance
5.输出操作
针对某一具体的序列,进行输出操作步骤
def operate(stack_out):
""" :param stack_out: 出栈序列 :return: 操作方法 当然也可以用于判断输出序列 """
stack_in = stack(len(stack_out))
j = 0
for i in range(len(stack_out)):
stack_in.push(i + 1)
print('push %d' % (i + 1))
while stack_in.noEmpty() and stack_out[j] == stack_in.top_value():
print('pop %d' % (stack_in.top_value()))
stack_in.pop()
j = j + 1
完整可运行代码
import copy
stack_perm = [] #记录入栈序列的全排列
#定义栈的数据结构
class stack():
def __init__(self, length):
self.length = length
self.top = -1
self.stack = []
def noEmpty(self): #判断不为空
if self.top == -1:
return False
else:
return True
def push(self,data):
if self.top >= self.length:
print('堆栈已满\n')
else:
self.top += 1
self.stack.append(data)
def top_value(self):
return self.stack[self.top]
def pop(self):
if self.noEmpty() is False:
print('堆栈是空的')
else:
self.stack.pop(self.top)
# print('弹出的数据为:%d' % (self.stack.pop(self.top)))
self.top -= 1
def operate(stack_out):
""" :param stack_out: 出栈序列 :return: 操作方法 当然也可以用于判断输出序列 """
stack_in = stack(len(stack_out))
j = 0
for i in range(len(stack_out)):
stack_in.push(i + 1)
print('push %d' % (i + 1))
while stack_in.noEmpty() and stack_out[j] == stack_in.top_value():
print('pop %d' % (stack_in.top_value()))
stack_in.pop()
j = j + 1
def judge_stack_byCompare(stack_out):
""" 通过入栈序列和出栈序列的比较 :param stack_out: 要判断的序列 :return: True or False """
stack_in = stack(len(stack_out))
j = 0
for i in range(len(stack_out)):
stack_in.push(i + 1)
while stack_in.noEmpty() and stack_out[j] == stack_in.top_value():
stack_in.pop()
j = j + 1
if stack_in.noEmpty():
return False #出栈序列不合法
else:
return True #出栈序列合法
def judge_stack_byOrder(stack_out):
""" 对于序列中的任意一个数其后面所有比他小的数应该是倒序的 :param stack_out: 要判断的序列 :return: True or False """
flag = 1
b = [None]
for i in range(len(stack_out)):
k = 0
for j in range(i+1,len(stack_out)):
if stack_out[i] > stack_out[j]: #在比data[i]的范围内讨论
if k == 0: #相当于记录一个基准值,再和其他小于data[j]的数讨论
b[k] = stack_out[j] #记录比data[i]更小的数
k += 1
else:
if stack_out[j] > b[0]:#出现了比记录值还大的数,更改标志
flag = 0
else:#记录更小的数
b[0] = stack_out[j]
return flag
def recursive(num):
"""计算全排列的个数"""
if num == 0:
return 1
else:
return num * recursive(num-1)
def perm_byOrder(stack_in,begin,end):
""" 求全排列 -> 调换顺序的方法 :param lis: 需要求全排列的序列 :param begin: 移动变量 :param end: 固定变量 :return: 全排列 """
if begin >= end: #结束标志
stack_perm.append(copy.copy(stack_in))
# print(stack_in)
else:
i = begin
for num in range(begin,end):
stack_in[num],stack_in[i] = stack_in[i],stack_in[num] #固定当前位置,在进行下一位的排列
perm_byOrder(stack_in,begin+1,end)
# 调用结束之后还需要回溯将交换位置的元素还原,以供其他下降路径使用(二叉树)
stack_in[num],stack_in[i] = stack_in[i],stack_in[num]
def perm_bySlice(stack_in):
""" 全排列 -> 利用切片的方法 :param stack_in: 需要求全排列的序列 :return: 全排列 """
if len(stack_in) == 1: #结束条件
return [stack_in]
reslut = []
for i in range(len(stack_in)):
s = stack_in[:i] + stack_in[i+1:] #stack_in[i] 不仅如此下一次递归
p = perm_bySlice(s)
for x in p:
reslut.append(stack_in[i:i+1] + x)
return reslut
def judge_for_m(stack_out):
""" 中转盲站的容量有限,最多能容纳的节数可能会小于输入数据的节数,因此需要在 合法出栈的队列的基础上,在进一步判断 ->【对于序列中的某一个数,其后有两个数小于它,则排除】 :param stack_out: 需要进行判断的出栈队列 :return: """
flag = 1
for i in range(len(stack_out)):
k = 0
for j in range(i+1,len(stack_out)):
if stack_out[i] > stack_out[j]: #找到后面比data[i]小的数
k += 1 #计数
if k >= m:
flag = 0
return flag
def stack_correct(stacks,n,m):
""" :param stacks: 全排列 :return: 对应m的条件下的合法出栈序列 """
stack_correct= [] #m>=n时的合法序列
stack_wrong = [] #m>=n时的非法序列
stack_correct_advance = [] #m<n时的合法序列
stack_wrong_advance = [] #m<n时的非法序列
for data in stacks:
# if judge_stack_byOrder(data):
if judge_stack_byCompare(data):
stack_correct.append(data)
else:
stack_wrong.append(data)
# 在合法的出栈序列的基础上进一步筛选
if m < n:
for data in stack_correct:
if judge_for_m(data):
stack_correct_advance.append(data)
else:
stack_wrong_advance.append(data)
return stack_correct,stack_wrong,stack_correct_advance,stack_wrong_advance
if __name__ == '__main__':
# 定义输入
n = int(input('请输入需要调度火车厢的节数n:'))
m = int(input('请输入中转盲站所能容纳的火车厢的节数m(可以>=n,也可以<n):'))
stackA = list(range(1, n + 1)) # 入栈序列 ->不用类也可以 顺序序列,如果其他序列,可以使用换成索引
perm_byOrder(stackA,0,len(stackA)) #计算全排列
# stack_perm = perm_bySlice(stackA) #计算全排列
print('可能出栈的序列为:',stack_perm)
stack_correct,stack_wrong,stack_correct_advance,stack_wrong_advance = stack_correct(stack_perm,n,m)
print('m>=n正确队列为:',stack_correct)
print('m>=n错误队列为:',stack_wrong)
print('m>=n正确序列应有个数(理论方法计算):',int((1/(n+1)) *(recursive(2*n)/(recursive(n)*recursive(n)))))
print('m>=n正确序列实际个数:',len(stack_correct))
print('m<n正确队列为【在m>=n正确队列的基础上】:',stack_correct_advance)
print('m<n错误队列为【在m>=n正确队列的基础上】:',stack_wrong_advance)
# 求某一具体序列的操作方法
print('===========================================================')
operate(stack_correct[0])
print('===========================================================')
注
- 内存问题
之前在定义记录全排列的数据的时候,定义了
stack_perm = [None] *recursive(num)
当num = 12 时 就出现了内存错误MemoryError,当num再大的时候,直接出现溢出错误OverFlow cannot fit ‘int’ into an index-sized integer:
因此这个地方需要注意
2.不提前定义大小直接使用append
会使电脑变得非常卡,占用内存太大
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/159380.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...