蓝桥杯–算法训练 筛选号码
一.比赛题目
1.题目要求
有n个人围成一圈,顺序排号(编号为1到n)。从第1个人开始报数(从1到3报数),凡报到3的人退出圈子。从下一个人开始继续报数,直到剩下最后一个人,游戏结束。
问最后留下的是原来第几号的那位。
举个例子,8个人围成一圈:
1 2 3 4 5 6 7 8
第1次报数之后,3退出,剩下:
1 2 4 5 6 7 8 (现在从4开始报数)
第2次报数之后,6退出,剩下:
1 2 4 5 7 8 (现在从7开始报数)
第3次报数之后,1退出,剩下:
2 4 5 7 8 (现在从2开始报数)
第4次报数之后,5退出,剩下:
2 4 7 8 (现在从7开始报数)
第5次报数之后,2退出,剩下:
4 7 8 (现在从4开始报数)
第6次报数之后,8退出,剩下:
4 7 (现在从4开始报数)
最后一次报数之后,4退出,剩下:
7.
所以,最后留下来的人编号是7。
2.输入与输出
输入:
一个正整数n,(1<n<10000)
输出:
一个正整数,最后留下来的那个人的编号。
输入样例:
8
输出样例:
7
二.分析过程
这道题我觉得用循环链表可能更加轻松简单,链表的删除操作也更加快捷,但是因为我的链表知识不到位,所以我只能用数组来模拟循环链表的操作;
1.整体分析
我们知道他是每数三个数就删掉一个数,被删掉的数字就不会再被数到,所以我们在数数字时需要模拟是否为有效数。
同时需要将有效数和已经被删掉的数字区分开。
2.主要代码分析
while(t<n-1){
int flag=0;
while(1)
{
if(!a[k%n])
flag++;
if(flag==3)
break;
k++;
}
k=k%n;
a[k]=1;
t++;
}
在上述代码中,flag代表着筛选这个行为,我们把删掉的数(a[i]就代表着第i个数)赋为1,而删掉之后的数就不会在进行筛选这个行为,所以要加上判断条件if(!a[k%n]
;
为什么这里是k%n
呢?
因为是模拟的循环筛选,所以我们需要始终在n这个范围里面来寻找;
同时最后一个问题:
因为只会剩下一个,所以我们设置一个变量t,每次删掉一个数组之后就t++一次,直到最后数组只剩下一个,退出循环。
三.代码
#include<bits/stdc++.h>
using namespace std;
//小杨也加入了万能头的行列
#define MAXN 10010
typedef long long ll;
int n,a[MAXN],t=0,k=0;
int main()
{
scanf("%d",&n);
memset(a,0,sizeof(a));
while(t<n-1){
//从0~n-1筛选(代表1~n个数)
//数组里面多于一个数没被删除的时候循环筛选
int flag=0;
while(1)
{
if(!a[k%n])
flag++;
if(flag==3)
break;
k++;
}
k=k%n;
//每次让k都等于k%n,不然k的数值会越来越大
a[k]=1;
//删掉第k+1个数
t++;
}
for(int i=0;i<n;i++)
if(a[i]==0)
{
printf("%d",i+1);
break;
//从第一个数依次向后搜寻,找到那个没被筛选掉的数
//即其值仍然是0的时候就+1输出,
//因为我的数组是从0开始的
//然后退出循环
}
return 0;
}