2016年江苏省信息与未来小学生程序设计比赛试题--洗牌

这是我第一天使用模板,若有不妥之处请指出!


前言

本文原题目为2016年“梦想杯”年江苏省信息与未来小学生程序设计比赛第三题。


一、题目描述 

小明把n(n为偶数)张牌按编号顺序1,2,3,...,n排成一堆,然后开始洗牌。一次洗牌的过程如下:

1 对于一堆牌编号为a1,a2,...,an,首先把牌分成均匀的两堆:a1,a2,..am,am+1,am+2,...an(其中m=n/2)

2然后按顺序交叉插入:a1,am+1,a2,am+2,...am,an

洗牌过程共重复了k次,请你标程帮助小明模拟洗牌的过程。牌堆的初始顺序是a1,a2,...,an。例如n=6,则牌堆的初始顺序是1,2,3,4,5,6。

首次洗牌时,会把牌分成1,2,3和4,5,6两堆,交叉插入后的结果为1,4,2,5,3,6。

第二次洗牌时,会把牌分成1,4,2和5,3,6两堆。交叉插入后得到1,5,4,3,2,6.

输入

三个整数n,k,i,分别表示牌的数量,洗牌的次数,牌的位置。

输出

n张牌洗过k次后,牌堆中第i张牌的编号。

样例组

样例1
输入 6 2 5
输出 2

样例2
输入 400 300 200
输出 368

数据范围

对于100%的数据,1<=n,k<=1000,1<=i<=n,题目保证n是偶数。

二、题目解决

1.大致思路

        从题目描述中可以发现,这道题目是一道模拟题,而且还是比较简单的那种。这道题目需要用数组模拟牌堆,将数组中从1到n的数赋初值(即这个数的位置),再用for循环模拟(模拟用的都是循环)k次洗牌过程,最后直接输出第i位上的数就完事。

2.模拟方法

       赋初值时的程序十分简单(而且都会写),这里就一笔带过了。

//设排队数组为p
for(int i=1;i<=n;i++)    p[i]=i;

        这道题目的主要难点在于模拟。这道题目的模拟过程说的很清楚,用双数组就可以了。所以这样的程序不花多久就能写出来(具体思路见题目描述)。

void cx(int n,int k,int &a[])
{
   int b[100001]; 
   for(int j=1;j<=k;j++)
    {
		memset(b,0,sizeof(b));
		for(int i=1;i<=n/2;i++)  b[i*2-1]=p[i];
		for(int i=n/2+1;i<=n;i++)  b[(i-n/2)*2]=p[i];
	    for(int i=1;i<=n;i++)  p[i]=b[i];
        //for(int i=1;i<=n;i++)  cout<<p[i]<<' ';
        //cout<<endl;
    }
}

        这样写是能在某些OJ上过的,但一把数据范围开大点就会时间超限。所以这段模拟程序需要优化。 我们可以以样例1为例(n=6),对其进行手工模拟(也可以直接用计算机直接输出,把注释符号去掉就行了)。

        模拟的表格如下(n=6):

洗牌次数牌堆
1154326
2132546
3123456
4142536

        再往后,可以发现,这些都是循环出现的。同时也可以发现,不管是什么数,第一位和最后一位永远不变,所以一个循环节共有n-2个个体(也就是牌洗完后的队列)。那么优化后的模拟过程是这样的:

//这里改用a作为洗牌数组
void cx(int n,int k,int a[])
{
    int b[100001];
    for(int j=1;j<=k%(n-2);j++)
	{
		memset(b,0,sizeof(b));
		for(int i=1;i<=n/2;i++)  b[i*2-1]=a[i];
		for(int i=n/2+1;i<=n;i++)  b[(i-n/2)*2]=a[i];
	    for(int i=1;i<=n;i++)  a[i]=b[i];
	}
}


具体标程

具体标程如下:

#include<bits/stdc++.h>
#define maxn 100001
using namespace std;
int n,m,o,a[maxn],b[maxn];
int main()
{
	cin>>n>>k>>m;
	for(int i=1;i<=n;i++)  a[i]=i;
	for(int j=1;j<=k%(n-2);j++)
	{
		memset(b,0,sizeof(b));
		for(int i=1;i<=n/2;i++)  b[i*2-1]=a[i];
		for(int i=n/2+1;i<=n;i++)  b[(i-n/2)*2]=a[i];
	    for(int i=1;i<=n;i++)  a[i]=b[i];
	}
	cout<<a[m];
	return 0;
}

这道题目就这么多。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值