题目描述
题目的相关描述不在这里赘述,请参考洛谷网站https://www.luogu.com.cn/problem/P1088
分析
首先很容易想到的是直接使用dfs进行全排列。因为可以注意到,全排列是按照字典序形式进行输出的,这样刚好符合题目的要求。那么这样只需要将每次排列的结果存进数组,再输出m+1位置的排列就可以了。但是这个方法存在两个明显的问题:
1.N可能接近10000,这导致数组会特别大,会超出空间限制。
2.全排列只适合有顺序的情况,如“1234”,“12345”,非常局限。
代码如下,只能应对非常简单的情况:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 10001;
int arr[N];
int n,m;
int ans[N][N];
int sol = 0;
bool st[N];
void dfs(int x)
{
if(x>n)
{
sol ++;
for(int i =1; i<=n;i++)
{
ans[sol][i] = arr[i];
}
return ;
}
for(int i =1;i<=n;i++)
{
if(!st[i])
{
st[i] = true;
arr[x] = i;
dfs(x+1);
arr[x] = 0;
st[i] = false;
}
}
}
int main()
{
scanf("%d",&n);
scanf("%d",&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&arr[i]);
}
dfs(1);
for(int i =1;i<=n;i++)
{
printf("%d ",ans[1+m][i]);
}
return 0;
}
进一步分析
现在需要解决的问题是:
1.如何从火星人给的那个序列开始向后枚举?
2.火星人手指太多了,如何避免时间或者空间爆了?
解决方法
1.在dfs的循环中添加一个判断,在没有方案的时候,将i的值设定为外星人给定的序列值。
2.设置一个变量,判断是否找到第m个序列,找到之后,在下一次搜索开始的时候直接return,避免了后序的搜索。
代码如下,成功通过:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 10001;
int mars[N]; //读入外星人给定的序列
int n,m;
int res =0;
bool st[N]; //状态数组,实现全排列
int arr[N]; //记录答案
bool return0 = false; //判断找到答案没
void dfs(int x)
{
if(return0) //找到了,直接退出
{
return;
}
if(x>n)
{
res++; //遍历每个排列
if(res==m+1) //如果是最后的排列,则输出
{
return0 = true;
for(int i=1;i<=n;i++)
{
printf("%d ",arr[i]);
}
}
return;
}
for(int i=1;i<=n;i++)
{
if(!res) //从外星人给定的序列开始
{
i = mars[x];
}
if(!st[i]) //常规dfs操作
{
st[i] = true;
arr[x] = i;
dfs(x+1);
st[i] = false;
arr[x] = 0;
}
}
}
int main()
{
scanf("%d",&n);
scanf("%d",&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&mars[i]);
}
dfs(1);
return 0;
}
此题完结!