这篇文章接上一篇,在牛客上关于一道约瑟夫环问题的解题笔记:
题目描述:
小明同学把1到n这n个数字按照一定的顺序放入了一个队列Q中。现在他对队列Q执行了如下程序:
while(!Q.empty()) //队列不空,执行循环
{
int x=Q.front(); //取出当前队头的值x
Q.pop(); //弹出当前队头
Q.push(x); //把x放入队尾
x = Q.front(); //取出这时候队头的值
printf("%d\n",x); //输出x
Q.pop(); //弹出这时候的队头
}
做取出队头的值操作的时候,并不弹出当前队头。
小明同学发现,这段程序恰好按顺序输出了1,2,3,…,n。现在小明想让你构造出原始的队列,你能做到吗?
输入描述:
第一行一个整数T(T ≤ 100)表示数据组数,每组数据输入一个数n(1 ≤ n ≤ 100000),输入的所有n之和不超过200000。
输出描述:
对于每组数据,输出一行,表示原始的队列。数字之间用一个空格隔开,不要在行末输出多余的空格.
输入例子:
4
1
2
3
10
输出例子:
1
2 1
2 1 3
8 1 6 2 10 3 7 4 9 5
题目分析:
出队的顺序是1 -> 2 -> 3 -> 4 -> 5 -> 6 ,1是第一个出队的。
题中的操作是:
- 先取出原队列队头元素 x = Q.front()
- 删除队头,这个队头已经被保留到x中了
- 将先前保留的队头放到队尾部
- printf新的队头,并且将这个队头删除,也就相当于kill这个队头
转换成约瑟夫问题:
我们看这个整个一次过程,就相当于从第一个元素开始,数到第二个元素,把这个第二个元素kill(移除队列)。之后再从这个被kill的元素的下一个元素(这时候已经成了新的队头),又开始数,到第二个又删除……..
那么就出来了:这是一个环内有6个数的约瑟夫环,q = 2的出环问题。
我们要求的,就是原先的队列的各个位置的值,也相当于每一步出队返回的下标,将其的值按照这个下标排列:
我们知道这时候的出队顺序是: 1 2 3 4 5 6
| | | | | |
而约瑟夫环中出队的顺序是: 2 4 6 3 1 5
如上的一一对应关系是:值1在原队列的2号位置,值2在原队列的4号位置,值3在原队列的6号位置.....
得出的结果就是:5 1 4 2 6 3
而题目中给出的答案也是这样。
所以我们得出一种比较简单的解题思路:
将1到n重新按照这个规则来排列一边,得到的就是与输出结果对应的出队顺序,可以借此还原先前的队列:
AC代码:
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
int main(void){
int n,k;
cin>>k;
while(k--){
cin>>n;
queue<int> temp;
vector<int> res;
for(int i = 0;i < n;i++)
{
temp.push(i+1);
}
while(!temp.empty()){
int x = temp.front();
temp.pop();
temp.push(x);
res.push_back(temp.front());
temp.pop();
}
vector<int> r(n+1);
for(int i = 0;i < n;i++){
r[res[i]] = i+1;
}
for(int i = 1;i <= n;i++)
if(i != n)
cout<<r[i]<<" ";
else
cout<<r[i];
cout<<endl;
}
return 0;
}
在讨论区还看到了比较好的方法:
#include <iostream>
#include <deque>
using namespace std;
int main()
{
int n, k;
cin >> k;
while(k > 0)
{
deque<int> q;
k--;
cin >> n;
for(int i = n; i > 0; i--)
{
q.push_front(i);
int t = q.back();
q.pop_back();
q.push_front(t);
}
for(int i = 0; i < q.size(); i++)
cout << q[i] << " ";
cout << endl;
}
}
这个是将出队顺序的数反过来执行一遍和出队相反的操作,相当于入队操作。
1.int x = temp.front(); 3.q.push_front(t);
temp.pop();
temp.push(x);
q.pop_back();
2.res.push_back(temp.front()); 2.int t = q.back();
3.temp.pop(); 1.q.push_front(i);
我来翻译一下:
1.取出头 3.将取出的尾放到头部
删掉头
2.把取出的头放到尾部 2.找到尾(被放到尾部的头),删掉尾
3.删掉新的头 1.压入头
那么这个过程就能看得懂了,这就是构造一个特殊的入队规则,相当于一种特殊的队列,和出队相反的规则。
好了,就到这里了,大家有什么更好的解法,欢迎提出,互相学习。