【bzoj1965】【AHOI2005】洗牌

13 篇文章 0 订阅
3 篇文章 0 订阅

题意

  有一副牌,共 n 张(n为偶数),每一次洗牌将其均分为上下两叠,然后取下叠第一张为新的第一张,上叠第一张为新的第二张,下叠第二张为新的第三张……求经过 m 次洗牌后,第l张牌的面值(初始为 1n

解法

模拟:
  这道题比较容易,只要能够一步一步地分析下去:
  首先可以知道,对于现在处于第 x 张的牌来说,有:
  ①.若其在上叠(xn2),那么洗牌后它将会去到 2x 的位置
  ②.若其在下叠( x>n2 ),那么洗牌后它将会去到 2x1 的位置
  所以,最暴力的做法:开一个滚动数组,不停地向前模拟,得分 20%
  如果将上面的式子变形,可以得到,对于现在处于第 x 张的牌来说,有:
  ①.若x为奇数,那么在洗牌前,这一张牌的位置是: n+x+12
  ②.若 x 为偶数,那么在洗牌前,这一张牌的位置是:x2
  所以高级一点的暴力就是采用递归,不停的求解,由于爆栈,所以得分 50%
  如果将递归改为递推,那么可以得到 70% ,最后的三个点会超时
  继续考虑优化:一个数 x 经过若干次变化之后,必定又会变回x,我们假设经过 n+1 次洗牌,那么 x 已经变化n+1次,而至多只有 n 个数,所以肯定又会变回去
  于是我们首先模拟找到变化的循环节,然后用m对循环节取余,然后剩下的余数部分再次暴力模拟即可

复杂度

O( klogm ), k 为一不确定数字,大概在11000

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值