【题目链接】
ybt 1929:【04NOIP普及组】火星人
洛谷 P1088 [NOIP 2004 普及组] 火星人
【题目考点】
1. 深搜回溯
2. STL next_permutation函数
头文件<algorithm>
函数定义:next_permutation(lb, ub, cmp)
lb:区间下界,为指针或迭代器
ub:区间上界,为指针或迭代器
cmp:函数或仿函数,指定比较规则,默认为less仿函数。
使区间[lb, ub)范围内的元素变为其下一个排列,复杂度为 O ( n ) O(n) O(n)
如果不传参数cmp,对整型序列取其下一个排列,其下一个排列就是将[lb, ub)区间内元素的全排列按字典序升序给出后,当前序列的下一个序列。
【解题思路】
火星人的手指排列就是一个1到n的排列,该排列表示的数就是该排列在按字典序排序的1到n的全排列中是第几个排列。
要想使该排列表示的数加上m,而后得到手指序列,就是要取按字典序排序的在1到n的全排列中,当前排列后的第m个排列。
解法1:深搜回溯
深搜求全排列是深搜回溯的模板题,相信各位同学都会写的。
#include <bits/stdc++.h>
using namespace std;
int num[10005], n, m;
bool vis[10005];
void dfs(int k)
{if(k > n){for(int i = 1; i <= n; ++i)cout << num[i] << ' ';cout << '\n';return;}for(int i = 1 ; i <= n; ++i) if(!vis[i]){num[k] = i;vis[i] = true;dfs(k + 1);vis[i] = false;}
}
int main()
{cin >> n >> m;dfs(1);return 0;
}
而现在是需要从深搜回溯求全排列的中间某状态出发,向后继续搜索,搜索到第m个排列时输出。
这个过程好比一个话剧,一开始就要从第二幕开始演,那么需要直接布置第二幕的舞台,相关演员也要好像第一幕都演完了的一样,直接开始第二幕演出。而不需要重新从第一幕开始演。
当前num数组的初值实际是通过输入得到的。我们需要让num数组当前的值是通过搜索得到的。
这是一个预先处理的过程,设bool类型量pre,初值为真,pre为真表示当前在进行预处理,使整个dfs过程达到刚好搜索出输入的num序列的状态。
调用dfs(1)
,搜索确定第一个数的值,如果第1个数的值是num[1]
,那么此时i循环到的值应该是num[1]
。
递归调用dfs(2)
,搜索确定第二个数的值,如果第2个数的值是num[2]
,那么此时i循环到的值应该是num[2]
…
调用dfs(k)
时,确定第k个数是num[k]
,那么该次调用中i应该循环到num[k]
。因此在dfs(k)
中,for循环i的初值应该设为num[k]
。
当k>n
时,进入搜索的递归出口,此时应该结束预处理,将pre设为假。
而后就开始正常的搜索排列的过程,非预处理状态下for循环i的初值应该为1。
设计数变量ct,每搜索到一个排列计数增加1。当计数达到m时,找到了输入序列后的第m序列,将其输出,而后结束递归。
解法2:使用next_permutation
STL中的next_permutation可以取一个序列的下一个排列。循环m次,每次循环取下一个排列,再将序列输出,即为结果。
【题解代码】
解法1:深搜回溯
#include <bits/stdc++.h>
using namespace std;
int num[10005], n, m, ct;
bool vis[10005], isPre = true;//isPre:是否在进行预处理
void dfs(int k)
{if(k > n){if(isPre){isPre = false;return;}if(++ct == m)//如果已经搜索到后面第m个排列,则输出结果 {for(int i = 1; i <= n; ++i)cout << num[i] << ' ';}return;}if(ct == m)//ct为m表示已经输出结果,结束搜索 return;for(int i = isPre ? num[k] : 1 ; i <= n; ++i) if(!vis[i]){num[k] = i;vis[i] = true;dfs(k + 1);vis[i] = false;}
}
int main()
{cin >> n >> m;for(int i = 1; i <= n; ++i)cin >> num[i];dfs(1);return 0;
}
解法2:使用next_permutation
#include <bits/stdc++.h>
using namespace std;
#define N 10005
int n, m, a[N];
int main()
{cin >> n >> m;for(int i = 1; i <= n; ++i)cin >> a[i];for(int i = 1; i <= m; ++i)next_permutation(a+1, a+1+n);for(int i = 1; i <= n; ++i)cout << a[i] << ' ';return 0;
}