Card Trick
Time Limit: 1000 MS Memory Limit: 65536 KB
64-bit integer IO format: %I64d , %I64u Java class name: Main
Description
![](http://acm.hrbust.edu.cn/vj/oj_static/poj/3032_desc_001.png)
The magician shuffles a small pack of cards, holds it face down and performs the following procedure:
- The top card is moved to the bottom of the pack. The new top card is dealt face up onto the table. It is the Ace of Spades.
- Two cards are moved one at a time from the top to the bottom. The next card is dealt face up onto the table. It is the Two of Spades.
- Three cards are moved one at a time…
- This goes on until the nth and last card turns out to be the n of Spades.
This impressive trick works if the magician knows how to arrange the cards beforehand (and knows how to give a false shuffle). Your program has to determine the initial order of the cards for a given number of cards, 1 ≤ n ≤ 13.
Input
On the first line of the input is a single positive integer, telling the number of test cases to follow. Each case consists of one line containing the integer n.
Output
For each test case, output a line with the correct permutation of the values 1 to n, space separated. The first number showing the top card of the pack, etc…
Sample Input
2 4 5
Sample Output
2 1 4 3 3 1 4 5 2
Source
题目大意:
我们要利用n张牌表演一个魔术,魔术的步骤是这样的:
将开头一张牌移动到最后,接着拿出这堆牌的第一张,这张牌的数字是1,将这张牌丢弃
将开头二张牌移动到最后,接着拿出这堆牌的第一张,这张牌的数字是2,将这张牌丢弃
将开头三张牌移动到最后,接着拿出这堆牌的第一张,这张牌的数字是3,将这张牌丢弃
……
将开头n张牌移动到最后,接着拿出这堆牌的第一张,这张牌的数字是n
问你,假如要成功地表演这个魔术,我们要将这n张牌排成什么样子?请输出这个排列。
解题分析:
一道简单的模拟题。一开始就有一个直观想法,枚举14个数字的全排列,然后依次模拟,判断哪一个解是符合魔术步骤的,输出它。但是我算了一下,14个数字的全排列是:87178291200,很明显,枚举必定超时。这题其实是要用逆向思维,首先我们必须明确一点,那就是:第一张牌一定在牌堆的第二个位置。其实这就是此题的突破口:每张牌的所在位置都是根据上一个状态推出的。让我来举个例子,对于样例n=4,模拟的过程就是:
设第一张牌的数字是x1,第二张牌为x2,第三张牌为x3,第四张牌为x4
当前牌堆序列为:x1 x2 x3 x4
将第一张牌移到最后,原序列为: x2 x3 x4 x1,也就是说x2一定是1
删除x2,继续模拟,将前两张牌移动到最后,原序列为:x1 x3 x4,也就是说x1一定是2
以此类推,我们可以还原出整个序列:2 1 4 3。
我使用了deque(双端队列)来模拟整个过程,因为双端队列支持pop_front和push_front,比较方便。
代码:
#include <iostream> #include <cstdio> #include <deque> using namespace std; int n,t,ans[20]; // ans数组中下标即扑克牌初始位置x1,x2...xn,其值即为扑克牌的值 void solve(){ deque<int> Q; //定义一个双向队列,在函数内部定义省时16ms for(int i=1;i<=n;i++) Q.push_back(i); int s=1; // s 即是输出扑克牌的值:1,2...n while(!Q.empty()){ //模拟魔术过程 for(int i=1;i<=s;i++){ int x=Q.front(); Q.pop_front(); Q.push_back(x); } ans[Q.front()]=s++; //ans[xi]对应的值s即为扑克牌的值 Q.pop_front(); } for(int i=1;i<=n;i++) //将ans数组输出 if(i!=n) printf("%d ",ans[i]); else printf("%d\n",ans[i]); } int main() { scanf("%d",&t); while(t--){ scanf("%d",&n); solve(); } return 0; }
补充知识:
双向队列:
头文件:#include <deque>
声明: deque<int> q;
pop_back()删除尾部的元素
pop_front()删除头部的元素
push_back()在尾部加入一个元素
push_front()在头部加入一个元素