问题:救济金发放(The Dole Queue, UVa 133
)
n(n<20)个人站成一圈,逆时针编号为1~n。有两个官员,A从1开始逆时针数,B从n开 始顺时针数。在每一轮中,官员A数k个就停下来,官员B数m个就停下来(注意有可能两个 官员停在同一个人上)。接下来被官员选中的人(1个或者2个)离开队伍。
输入n,k,m输出每轮里被选中的人的编号(如果有两个人,先输出被A选中的)。例 如,n=10,k=4,m=3,输出为7 3, 2 6, 8 10, 9 5, 1, 4。注意:输出的每个数应当恰好占3列。
分析:利用数组来表示每一个位置,用零标记被抽走的人防止再次被抽
C语言代码:
#include<stdio.h>
#define maxn 25
int n,p1,p2,k,m,a[maxn];//p1,p2记录两官员当前位置
int p1_guess(int p1,int k)//A官员行走函数
{
while(k--)
{
do
{
p1=(p1-1+n-1)%n+1;//计算A的移动
}while(a[p1]==0);//走到下一个非零数字
}
return p1;
};
int p2_guess(int p2,int m)//B官员行走函数
{
while(m--)
{
do
{
p2=(p2+1+n-1)%n+1;//计算B的移动
}while(a[p2]==0);//走到下一个非零数字
}
return p2;
};
int main()
{
while(scanf("%d%d%d",&n,&k,&m)==3&&n)
{
int left=n;//记录剩下的人
for(int i=1;i<=n;i++)
a[i]=i;
p1=1;
p2=n;
while(left)
{
p1=p1_guess(p1,k);
p2=p2_guess(p2,m);
if(p1==p2)
printf("%3d",p1);
else
{
printf("%3d",p1);
printf("%3d",p2);
}
a[p1]=a[p2]=0;//用零来补上被抽中的人防止再被抽
left--;
if(left)
printf(",");
}
printf("\n");
}
return 0;
}
p1_guess()与p2_guess其实可以合并,用+1表示顺时针,-1表示逆时针,合并后代码如下:
#include<stdio.h>
#define maxn 25
int n,p1,p2,k,m,a[maxn];//p1,p2记录两官员当前位置
int guess(int p,int len,int t)//B官员行走函数
{
while(t--)
{
do
{
p=(p+len+n-1)%n+1;//计算B的移动
}while(a[p]==0);//走到下一个非零数字
}
return p;
};
int main()
{
while(scanf("%d%d%d",&n,&k,&m)==3&&n)
{
int left=n;//记录剩下的人
for(int i=1;i<=n;i++)
a[i]=i;
p1=1;
p2=n;
while(left)
{
p1=guess(p1,-1,k);
p2=guess(p2,+1,m);
if(p1==p2)
printf("%3d",p1);
else
{
printf("%3d",p1);
printf("%3d",p2);
}
a[p1]=a[p2]=0;//用零来补上被抽中的人防止再被抽
left--;
if(left)
printf(",");
}
printf("\n");
}
return 0;
}
思考:为何是围成一圈,可一维数组可以实现?
诀窍其实在此公式:p=(p+len+n-1)%n+1;
巧妙的解决了这个问题