您的位置:首页 > 其它

XTU 洗纸牌问题

2017-08-29 21:45 162 查看

纸牌

题目描述

有2N张纸牌,标号从1到2N。一次洗牌,可以将牌的排列改成n+1,1,n+2,2,...,n,2n。请问这样洗牌几次,可以让牌回到原始的状态。比如N=2时,1234->3142->4321->2413->1234 一共洗4次。

输入

每行输入一个整数N(1≤N≤10,000),N如果为0,表示输入结束,不需要处理。

输出

每行输出一个样例的结果。

样例输入

1

2

3

4

5

6

0

样例输出

2

4

3

6

10

12

 

分析:

题目给的N达到了100,000,一般的方法极有可能超时。

下面是最先想到的成功超时的方法:


方法一(极有可能超时)

#include <iostream>//1210 Eddy's 洗牌问题 模拟
#include <string>
using namespace std;

int n,m;
void change(string &str )
{
string a;
for(int i=1;i<=n;i++)
{
cc++;
a=str[n+i];
str.erase(n+i,1);
str.insert(i*2-1,a);
}
}

int main()
{
string str,tmpstr;
while(cin>>n)
{
str.resize(n*2+1);
for(int i=1;i<=n*2;i++)
str[i]=i+'0';
m=0;
tmpstr=str;
while(1)
{
change( str );
m++;
if(tmpstr==str) break;
}
cout<<m<<endl;
}
return 0;
}


后经过一番思考和演算……我发现这道题是有规律的。

规律就是1的位置,很容易发现1的位置是从1->2->4->8……如果超过数尾则从头偏移,总之只要经过若干次移动,1再次移动在1的位置,就能够保证洗牌洗回了原序列。


方法二(简单)

#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
while(scanf("%d",&n),n)
{
int cnt=0;
int pos=1;
do
{
if(pos<=n) pos*=2;//记录1的位置:1-2-4-8-...直到01串尾
else pos=(pos*2-1)%(2*n);//相当于环形存储(求余操作)
cnt++;
}
while(pos!=1);
printf("%d\n",cnt);
}
return 0;
}


【另】

当然方法不止这些!比如这种,只追踪第一张牌的位置,只有当他在第n+1的位置时下一次才会回到初始位置.你并没有考虑其他牌的位置,因为他们都是相关的,位置不会乱.
4000

#include <stdio.h>
int main()
{
int n, x, c;
for(; scanf("%d", &n) != EOF; printf("%d\n", c))
for(c = x = 1; (x = x>n ? (x-n)*2-1 : x*2) - 1; c++);
return 0;
}


--------------------

【附:一文一图】

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: