考虑如下问题,编号依次为1,2,—n的n个人依照编号次序排成环形,现要将这n个人重新排列成环形,使得在原先的环形排列中彼此相邻的两个人在新的环形排列中不相邻,试设计算法枚举满足要求的所有新的环形排列。
注意在满足要求的任意一个环形排列中以排列中n个人的每一个人作为起点顺时针或逆时针绕排列一圈 回到作为起点的人所形成的序列对应的都是同一种合法的环形排列,因此在枚举过程要求仅保留从n个人中仅一个人出发形成的顺时针(或逆时针)排列,注意是在算法运行过程中剔除多余环形排列,而不是从生成的含有重复结果的排列集合中剔除重复环形排列
C++代码如下:
#include <iostream>
#include <vector>
using namespace std;
size_t N = 9;
size_t pre_pos(size_t pos)
{
if (pos == 0)
return N - 1;
return pos - 1;
}
size_t next_pos(size_t pos)
{
if (pos == N - 1)
return 0;
return pos + 1;
}
size_t find_next_pos(size_t num, vector<size_t> &pos_of_Num, vector<size_t> &Num_in_pos, size_t cur_pos, size_t count)
{
size_t pre = pre_pos(cur_pos);
size_t next = next_pos(cur_pos);
for (; num < N; ++num)
{
if (pos_of_Num[num] == N)
{
size_t adj_num_1 = pre_pos(num);
size_t adj_num_2 = next_pos(num);
if ((pos_of_Num[adj_num_1] == N || pos_of_Num[adj_num_1] != pre && pos_of_Num[adj_num_1] != next) &&
(pos_of_Num[adj_num_2] == N || pos_of_Num[adj_num_2] != pre && pos_of_Num[adj_num_2] != next))
{
if (count != 0 && cur_pos == N - 1 && Num_in_pos[1] > num)
return N;
return num;
}
}
}
return N;
}
int main()
{
vector<size_t> pos_of_Num(N, N); //各数的位置
vector<size_t> Num_in_pos(N, N); //各位置上的数
size_t i = 1;
size_t count = 0;
pos_of_Num[0] = 0;
Num_in_pos[0] = 0;
while (true)
{
bool end = Num_in_pos[i] == N;
size_t r;
if ((r = find_next_pos(end ? 1 : Num_in_pos[i] + 1, pos_of_Num, Num_in_pos, i, count)) == N)
{
if (i == 1)
break;
if (Num_in_pos[i] != N)
{
pos_of_Num[Num_in_pos[i]] = N;
Num_in_pos[i] = N;
}
--i;
continue;
}
if (!end)
pos_of_Num[Num_in_pos[i]] = N;
Num_in_pos[i] = r;
pos_of_Num[r] = i;
if (i != N - 1)
{
++i;
}
else
{
++count;
cout << "第" << count << "个解:" << endl;
cout << "pos:";
for (size_t i = 1; i <= N; ++i)
{
cout << i << " ";
}
cout << endl;
cout << "num:";
for (size_t i = 0; i < N; ++i)
{
cout << Num_in_pos[i] + 1 << " ";
}
cout << endl;
cout << endl;
}
}
cout << "共有" << count << "个解" << endl;
return 0;
}