OJ演练--救济金发放(循环)

(n<20)个人站成一圈,逆时针编号为1~n。 有两个官员,A从1开始逆时针数,B从n开始顺时针数。 在每一轮中,官员A数k个就停下来,官员B数m个就停下来(注意有可能两个官员停在同一个人上)。 接下来被官员选中的人(1个或者2个)离开队伍。输入n,k,m输出每轮里被选中的人的编号(如果有两个人,先输出被A选中的)。 例如,n=10,k=4,m=3,输出为4 8, 9 5, 3 1, 2 6, 10, 7。 注意:输出的每个数应当恰好占3列。

思路:
救济金的问题抽象出来就是几个人围成一个圈坐,给每一个人编号,一个人从1开始,一个人从n开始,从一开始的点到k时,出列一人,n逆时针点人,点到m出列一人。如果我们出列用删除操作,则大大的降低了效率,我们将删除掉的人用0来代替,当我们遇到0时直接跳过。

我们数数时,在起始位置时,由于是一个圆圈,我们可以设置从一数的时候从最后一个位置起始,最后位置数数的时候从起始位置起。即p从1开始,把1包含里面,所以从n开始逆时针数;同理,为了把n包含在里面,q从1开始顺时针数。

while(t–)执行的循环次数是t次;每次走p = (p + q + n-1)%n + 1(把顺时针的合并所以加上了n-1)。

#include"stdio.h"
#define max 20
int n,k,m;
int a[max];

int go(int p,int q,int t){
    while(t--){
        do{
            p=(p+q+n-1)%n + 1;
        }while(a[p]==0);
    }
    return p;
}

int main()
{
    int i;
    int p;
    int q;
    int left_num;
    scanf("%d %d %d",&n,&k,&m);

    for(i=1;i<=n;i++){
        a[i]=i;
    }
    left_num = n;
    p=n;
    q=1;
    while(left_num)
    {
        p=go(p,1,k);
        q=go(q,-1,m);
        printf("%3d",p);
        left_num--;
        if(q!=p)
        {
            printf("%3d",q);
            left_num--;
        }
        a[p] = a[q] = 0;//标记走过的位置
        if(left_num) printf(",");
    }
    printf("\n");
}

猜你喜欢

转载自blog.csdn.net/qq_15437629/article/details/79720950