问题描述:
假设停在铁路调度站入口处的车厢序列的编号依次为1,2,3……N。设计一个程序,求出所有由此输出的长度为N的车厢序列。
算法综述:
上一篇博文实现了解决车厢问题的递归算法。本文试图对N个车厢的全排列进行筛选,选出可能的序列。
必须声明,产生全排列的算法也有两种:递归和非递归,本文使用的是递归产生全排列。全排列的非递归算法后面再说。。。
调度站的本质是一个栈,LIFO决定了输出序列必须满足:
若以编号0,1,2,3…n-1的顺序进行调度,在一个输出序列x0,x1,x2……x(n-1)中,对于每个xi,序列x(i+1)……x(n-1)中,编号小于xi的车厢必须是递减的。
对每一个序列从后往前检查,max表示没检查到的最大元素。max初始为n-1,在没有检查到max之前(说明max已经出站),比max小的元素都应该是有序的,检查到max后,限制条件有所放宽,寻找新的max,继续检查
代码实现:
*******Stack.h*******
为了简洁点,省去了,其他文章中有。。。。
**********************
*****main.cpp*******
#include<iostream>
#include"Stack.h"
using namespace std;
int sum = 0;//计数器,每产生一个排列就+1
void Swap(int &m, int &n)//交换两个元素
{
int temp = m;
m = n;
n = temp;
}
//将a中的合法序列打印出来
void Result(int a[], const int n)
{
cout << ++sum << endl;
for (int i = 0; i < n; i++)
cout << a[i];
cout << endl;
}
//在一个序列中未进行合法性检查的元素中,选出最大值,序列中该值后的比它小的元素应是递减的
int FindMax(int flag[], const int n)
{
for (int i = n - 1; i >= 0; i--)
if (flag[i] == 0)
return i;
return 0;
}
//在对一个序列进行合法性检查之前,将out和stack置为初始状态
void Reset(int flag[], const int n, Stack<int>& stack)
{
int temp;
while (stack.Top() != -1)
stack.Pop(temp);
for (int i = 0; i < n; i++)
flag[i] = 0;
}
//缩减堆栈至其中只含有小于max的元素序列
void Reduce(Stack<int>& stack, int max)
{
int temp;
while (stack.Top()>max)
stack.Pop(temp);
}
//检查全排列序列的合法性
bool Check(int a[], int flag[], const int n, Stack<int>& stack)
{
Reset(flag, n, stack);
int max = n - 1;//未检查的最大车厢
//从后往前开始检查
for (int i = n - 1; i >= 0; i--)
{
if (a[i] == max)
{
flag[a[i]] = 1;//置1,表示该元素已检查过
max = FindMax(flag, n);
Reduce(stack, max);
}
else
{
if (a[i] < stack.Top())
return false;//不是递减,序列不合法
else//a[i]<max&&a[i]>stack.Top() 没检查到max,压栈
{
stack.Push(a[i]);
flag[a[i]] = 1;
}
}
}
return true;
}
//产生全排列,并对每一个序列进行合法性检查
void Perm(int a[], int flag[], int k, const int n, Stack<int>& stack)
{
if (k == n - 1)
{
//如果合法,输出序列
if (Check(a, flag, n, stack))
Result(a, n);
}
else
{
for (int i = k; i <= n - 1; i++)
{
Swap(a[k], a[i]);
Perm(a, flag, k + 1, n, stack);
Swap(a[k], a[i]);
}
}
}
void main()
{
int n;//车厢数目
int *a;//原始铁轨
int *flag;//已经检查过的元素
cin >> n;
a = new int[n];
flag = new int[n];
for (int i = 0; i < n; i++)
{
a[i] = i;
flag[i] = 0;//初始状态,都没检查
}
Stack<int> stack;
stack.Push(-1);//初始值为0,打底,用于s中没有元素时,与栈顶元素的比较
Perm(a, flag, 0, n, stack);
delete[]a;
delete[]flag;
system("pause");
}
非递归解法与递归解法的结果完全一致
n=4:14种
n=5:42种
n=6:132种
n=7:429种
n=8:1430种