算法习题---4.3救济金发放(UVa133)

一:题目

(n<20 )个人站成一圈,逆时针编号为1~n。
有两个官员,A从1开始逆时针数,B从n开始顺时针数
在每一轮中,官员A数k个就停下来,官员B数m个就停下来(注意有可能两个官员停在同一个人上)。接下来被选中的人离开队伍。 输入n,k,m输出每轮被选中的人的编号(如果有两个人,先输出A的)每个输出数字正好占3列。

二:实现思路

A从数组首部向后遍历(若是该位置还有人,则步数加一,否则不改变步数),当遍历到最后,则转回首部继续遍历。
B从数组尾部向前遍历(...),若是遍历到首部,则转回尾部继续向前遍历

三:代码实现

int member[21] = { 0 };    //为了方便理解,从下标1开始到下标20被利用
int n, k, m;

/*
input:
    int pos        //当前位置
    int dict    //谁要走
    int step    //走的步数
output:
    走之后的当前位置
*/
int donate(int pos, int dict, int step)
{
    while (step--)
    {
        do 
        {
            pos = (pos + dict + n - 1) % n + 1;
        } while (member[pos]==0);
    }
    return pos;
}

void func04()
{
    freopen("data.in", "r", stdin);
    freopen("data.out", "w", stdout);

    while (1)
    {
        //开始读取数据
        scanf("%d%d%d", &n, &k, &m);
        if (n == 0 && k == 0 && m == 0)
            break;    //结束

        int pos1 = n, pos2 = 1, count = n;

        //将对应人数置为1,表示没有领取救济金
        for (int i = 1; i <= n; i++)
            member[i] = 1;

        while (count)
        {
            pos1 = donate(pos1, 1, k);
            pos2 = donate(pos2, -1, m);

            printf("%3d", pos1);
            count--;
            if (pos1 != pos2)
            {
                printf("%3d", pos2);
                count--;
            }
            member[pos1] = member[pos2] = 0;
            if (count)
                printf(", ");
        }
        printf("\n");
    }

    freopen("CON", "r", stdin);
    freopen("CON", "w", stdout);
}

四:代码分析

(一):位置计算

pos = (pos + dict + n - 1) % n + 1;
当方向dict为正数时,pos + dict + n - 1 = pos+n
故pos = (pos+n)%n + 1向前走一步pos在(1,n)范围
当dict为负数时,pos + dict + n - 1 = pos+n-2
pos = (pos+n-2)%n+1 = pos - 2+1 = pos-1;向后走一步pos范围在(1,n)

(二):初始值

pos1 = n, pos2 = 1
我们传递到处理函数中的位置初值,应该是我们开始处理的前一个数据。
我们返回应该处理的数据的位置

五:应该将逻辑和数据分开《重点》

donate函数只复制找到我们需要处理的位置,main函数则继续数据修改。这样为我们后面的调试,和解耦合带来的好处

 

posted @ 2019-05-09 12:30  山上有风景  阅读(409)  评论(0编辑  收藏  举报