我觉得筛法的本质是将符合某特定条件的数标记,并以标记为依据将这些数跳过。
对于幸运的编号这一题,我觉得就可以运用筛法的思想。
描述: 有n个人围成一圈,顺序编号。从第一个人开始报数(从1到m),凡报到m的人退出。问最后一个人的编号是多少?
输入:
输入两个正整数n和m
输出: 最后一个人的编号。
输入样例: 5 2
输出样例: 3
这道题比较难的地方我觉得主要有两个:
1.围成一圈怎么解决?
2.一个人退出后紧接着下一个人重新开始报数,必须跳过之前退出的人。
为了解决第一个问题,我的想法是把圈掰成列,定义一个数组a[j],数组的长度由过程中要转的圈数(可以比要转的实际圈数大)乘以总人数确定,j除以人数所得余数是这个人的编号。
如:有5人,每次报2个数时;
最少转3圈,数组就是a[15];
a[2],a[7],a[12]都代表编号为2的人。
为了解决第二个问题,我觉得应该使用筛法,将所有报数报到m的人即所有代表他们的数都标记为0。
如编号为2的人第一个报到2,将a[2],a[7],a[9]都标记为0。
#include<stdio.h>
int main()
{
int m,n,c,count=0,b,i,j,a[10000];
scanf("%d%d",&m,&n);
c=(m/n+1)*(m-1)*m;
b=m-1;
for(i=1;i<=c;i++)
{
a[i]=1;
}
for(i=1;1<=c;i++)
{
if(a[i])
{
count++;//相当于在报数
}
if(count==n)//当报数报道n时
{
a[i]=0;
count=0;
b-=1;//记录筛出了多少个人
for(j=1;j<=c;j++)
{
if(j%m==i%m)
{
a[j]=0;
}
}
}
if(b==0)//当筛出到只剩一个人时,输出并退出循环
{
for(j=i;j<=c;j++)
{
if(a[j])
{
printf("%d\n",j%m);//bug:j为m的倍数时输出的是零。
break;
}
}
break;
}
}
}
选太子用的也是一样的想法。
描述:
某皇帝有2m个儿子,现在要从中选出一个做太子,皇帝不知道该把那一个皇子立为太子,于是决定用下面的方法来选出太子,设每个太子的编号分别1、2、3、…、2m,按顺时针方向站成一个圆圈,现在从1号太子开始按顺时针方向数,数到第n个人,把他淘汰出局,然后从他的下一个人开始上述过程,当第m个人被淘汰时,转变方向继续从1开始数,重复上述过程,最后剩下的皇子将被立为太子。现在请你写一个程序,计算出几号皇子将被立为太子。
输入:
输入两个正整数m n
Input two positive integer.
输出:
输出太子的编号
Output the number.
输入样例:
3 2
输出样例:
1
#include<stdio.h>
int main()
{
int m,n,c,count=0,b,i,j,a[10000];
scanf("%d%d",&m,&n);
c=(2*m/n+1)*2*m*m;
b=0;
for(i=1;i<=c;i++)
{
a[i]=1;
}
for(i=1;1<=c;i++)
{
if(a[i])
{
count++;
}
if(count==n)
{
a[i]=0;
count=0;
b+=1;
for(j=1;j<=c;j++)
{
if(j%(2*m)==i%(2*m))
{
a[j]=0;
}
}
}
if(b==m)
{
break;
}
}
i=c-2*m+(i-1)%(2*m);//不能用之前的i,因为可能出现i--到0,但选太子过程还没有结束的情况,所以要用能表示当前编号最大的i
for(i=i;i>0;i--)
{
if(a[i])
{
count++;
}
if(count==n)
{
a[i]=0;
count=0;
b+=1;
for(j=1;j<=c;j++)
{
if(j%(2*m)==i%(2*m))
{
a[j]=0;
}
}
}
if(b==(2*m)-1)
{
for(j=i;j>0;j--)
{
if(a[j])
{
if(j%(2*m)==0) printf("%d\n",2*m);
else printf("%d\n",j%(2*m));
break;
}
}
break;
}
}
}