题目链接T92
一、求解过程
首先写出来一个全排列
void print(int n)//打印全排列
{
if(n == N + 1)
{
for(int i = 1;i <= N;++i)
{
cout << ans[i] << ' ';
}
cout << '\n';
return;
}
for(int i = 1;i <= N;++i)
{
if(!chosen[i])
{
ans[n] = i;
chosen[i] = true;
print(n + 1);
chosen[i] = false;
}
}
}
输出如下:
3
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
在此基础上限制一下排列的位数,print()函数里增加一个sit变量用以控制输出位数
#include<bits/stdc++.h>
using namespace std;
int ans[20];
int order = 0;
int N;
bool chosen[20];
void print(int n, int sit)//打印全排列
{
if(n == sit + 1)
{
for(int i = 1;i <= sit;++i)
{
cout << ans[i] << ' ';
}
cout << '\n';
return;
}
for(int i = 1;i <= N;++i)
{
if(!chosen[i])
{
ans[n] = i;
chosen[i] = true;
print(n + 1,sit);
chosen[i] = false;
}
}
}
int main()
{
cin >> N;
for(int i = 1;i <= N;++i)
{
print(1, i);
}
// print(1);
// for(int i = 0;i < N; ++i){
// cout<<ans[i];
// }
}
输出结果如下:
3
1
2
3
1 2
1 3
2 1
2 3
3 1
3 2
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
接着要想办法去除不满足要求的逆序排列,这时如果考虑简单的在输出部分加上一个判断函数,会导致T,卡在N=12,如下:
bool judge(int sit) // 判断逆序
{
for(int i = 1;i < sit; ++i)
{
if(ans[i] > ans[i + 1]) return false;
}
return true;
}
void print(int n, int sit)//打印全排列
{
if(n == sit + 1)
{
if(judge(sit))
{
for(int i = 1;i <= sit;++i)
{
cout << ans[i] << ' ';
}
cout << '\n';
}
return;
}
for(int i = 1;i <= N;++i)
{
if(!chosen[i])
{
ans[n] = i;
chosen[i] = true;
print(n + 1,sit);
chosen[i] = false;
}
}
}
那只能考虑修改print函数。输出会产生全排列的情况原因在于,未修改的print函数是对如下的解答树进行遍历。
下面就要考虑剪掉不符合条件的解答。正确的解答树应当在每一个递归层都是从比上一层更大的数开始。如下:
可以在print函数中增加一个变量用以控制每层递归的分支。修改后如下:
#include<bits/stdc++.h>
using namespace std;
int ans[20];
int order = 0;
int N;
bool chosen[20];
void print(int n,int ss, int sit)//
{
if(n == sit + 1)
{
for(int i = 1;i <= sit;++i)
{
cout << ans[i] << ' ';
}
cout << '\n';
return;
}
for(int i = ss;i <= N;++i)//增加ss后下一层只能从比上一层数字更大的范围内选择
{
if(!chosen[i])
{
ans[n] = i;
chosen[i] = true;
print(n + 1,i + 1,sit);
chosen[i] = false;
}
}
}
int main()
{
cin >> N;
cout << endl;
for(int i = 1;i <= N;++i)
{
print(1,1,i);
}
// print(1);
// for(int i = 0;i < N; ++i){
// cout<<ans[i];
// }
}
这样就A了。
二、蓝皮书正解
蓝皮书中采用vector容器作为保存已选择的数据的方式。相较于数组,vector更方便删除和添加数据。
#include<bits/stdc++.h>
using namespace std;
vector<int> ans;
int N;
void print(int n)
{
if(n == N + 1){
for(int i = 0;i < ans.size();++i){
cout << ans[i] << ' ';
}
cout << '\n';
return;
}
//不选择当前的数
print(n + 1);
//选择当前的数
ans.push_back(n);
print(n + 1);
//清空已选择内容
ans.pop_back();
}
int main()
{
cin >> N;
print(1);
}
输出结果如下:
3
3
2
2 3
1
1 3
1 2
1 2 3
在这种构造下,解答树转化为了如下的一颗二叉树,求解的过程实际上等同于对这颗二叉树进行先序遍历。0代表选择,1代表不选择。
三、采用非递归方式求解
对于输入的N,采用N位二进制数来表示状态,如果选择第n位,那么对应二进制数的位置为1。例如,N=3时,选择1,2,不选择3,对应的二进制数位011。
总共需要枚举 2^N 个状态。也就是 0~N-1 位二进制数。
判断某一位是否为1,i >> j & 1
。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin >> n;
for(int i = 0;i < 1 << n; ++i)//二进制0~n-1位
{
for(int j = 0;j < n; ++j){
if(i >> j & 1){//如果该位为1
cout << j + 1 << ' ';
}
}
cout << endl;
}
}