题意
??有一副牌,共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;
}