题意
有一副牌,共 n 张(
n 为偶数),每一次洗牌将其均分为上下两叠,然后取下叠第一张为新的第一张,上叠第一张为新的第二张,下叠第二张为新的第三张……求经过 m 次洗牌后,第l 张牌的面值(初始为 【1,n】 )
解法
模拟:
这道题比较容易,只要能够一步一步地分析下去:
首先可以知道,对于现在处于第 x 张的牌来说,有:
①.若其在上叠(x≤n2 ),那么洗牌后它将会去到 2∗x 的位置
②.若其在下叠( x>n2 ),那么洗牌后它将会去到 2∗x−1 的位置
所以,最暴力的做法:开一个滚动数组,不停地向前模拟,得分 20%
如果将上面的式子变形,可以得到,对于现在处于第 x 张的牌来说,有:
①.若x 为奇数,那么在洗牌前,这一张牌的位置是: n+x+12
②.若 x 为偶数,那么在洗牌前,这一张牌的位置是:x2
所以高级一点的暴力就是采用递归,不停的求解,由于爆栈,所以得分 50%
如果将递归改为递推,那么可以得到 70% ,最后的三个点会超时
继续考虑优化:一个数 x 经过若干次变化之后,必定又会变回x ,我们假设经过 n+1 次洗牌,那么 x 已经变化n+1 次,而至多只有 n 个数,所以肯定又会变回去
于是我们首先模拟找到变化的循环节,然后用m 对循环节取余,然后剩下的余数部分再次暴力模拟即可
复杂度
O( klogm ), k 为一不确定数字,大概在
【1,1000】
20分代码
#include<iostream>
#include<cstdlib>
#include<cstdio>
using namespace std;
const int MAXN=10000010;
int w[2][MAXN];
int n,m,l,cur;
int main()
{
scanf("%d%d%d",&n,&m,&l);
for(int i=1;i<=n;i++) w[0][i]=i;
while( m-- )
{
for(int i=1;i<=n/2;i++) w[cur^1][2*i]=w[cur][i];
for(int i=1;i<=n/2;i++) w[cur^1][2*i-1]=w[cur][i+n/2];
cur^=1;
}
printf("%d\n",w[cur][l]);
return 0;
}
50分代码
#include<iostream>
#include<cstdlib>
#include<cstdio>
using namespace std;
const int MAXN=10000010;
int n,m,l,ans;
void work(int k,int pos)
{
if( !k ) { ans=pos;return ; }
if( pos%2 ) work( k-1,n/2+(pos+1)/2 );
else work( k-1,pos/2 );
}
int main()
{
scanf("%d%d%d",&n,&m,&l);
work( m,l );
printf("%d\n",ans);
return 0;
}
70分代码
#include<iostream>
#include<cstdlib>
#include<cstdio>
using namespace std;
int n,m,l;
int main()
{
scanf("%d%d%d",&n,&m,&l);
for(int i=1;i<=m;i++)
{
if( l%2 ) l=n/2+(l+1)/2;
else l=l/2;
}
printf("%d\n",l);
return 0;
}
100分代码
#include<iostream>
#include<cstdlib>
#include<cstdio>
#define Lint long long int
using namespace std;
Lint n,m,l,x,cnt=1;
int main()
{
scanf("%d%d%d",&n,&m,&l);
if( l%2 ) x=n/2+(l+1)/2;
else x=l/2;
while( x!=l )
{
cnt++;
if( x%2 ) x=n/2+(x+1)/2;
else x=x/2;
}
m=m%cnt;
for(int i=1;i<=m;i++)
if( l%2 ) l=n/2+(l+1)/2;
else l=l/2;
printf("%d\n",l);
return 0;
}