算法习题---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函数则继续数据修改。这样为我们后面的调试,和解耦合带来的好处

原文地址:https://www.cnblogs.com/ssyfj/p/10837780.html

时间: 2024-10-10 09:23:15

算法习题---4.3救济金发放(UVa133)的相关文章

UVa 133 救济金发放

题意:所有n个人围成一个圈,一边从1号开始逆时针数k个,出局:一边从n号开始顺时针数m个,出局.两个同时发生.如果这两个数到同一个人,就这个人出局. facing inwards 面朝里: counter 相反地,clockwise 顺时针,counter-clockwise 逆时针 思路:双向循环链表来模拟.按以前书上介绍的实现的,有一个头指针,首尾都指向它.但这里由于要找第k个.第m个,需要进行头指针的判断,所以觉得这里不增加头指针比较好,或者头指针只指向头结点,而头结点和尾结点是互指的,不

算法入门经典-第四章 例题4-3 救济金发放

救济金的问题抽象出来就是几个人围成一个圈坐,给每一个人编号,一个人从1开始,一个人从n开始,从一开始的点到k时,出列一人,n逆时针点人,点到m出列一人.如果我们出列用删除操作,则大大的降低了效率,我们将删除掉的人用0来代替,当我们遇到0时不点人. 使用两个方法来分别逆时针顺时针点人,如果是0,则跳过 n(n<20)个人站成一圈,逆时针编号为1-n.有两个官员,A从1开始逆时针数,B从n开始顺时针数.在每一轮中,官员A数k个就停下来,官员B数m个就停下来(注意有可能两个官员停在同一个人上).接下来

救济金发放(UVa133)

题目具体描述见:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=69 C++11代码如下: 1 #include<iostream> 2 #include<iomanip> 3 using namespace std; 4 int que[25]; 5 int n, k, m; 6 7 int go(int p, i

2015/10/13 算法习题:最大子列和问题

已经正式开始学习数据结构和算法,先学了网易云课堂上的浙江大学的数据结构课,是陈越和何钦铭上的,了解了什么是数据结构和算法后,学习了一些时间空间复杂度分析的技巧,结合之前马虎掌握的学习,先从简单的题目入手学习. 题目是这样的: 给定了一个n个整数组成的序列,求它各个子列中,子列和最大的值. 输入:输入n个整数组成的序列 要求输出最大子列和. 示例: 输入: -2 11 -4 13 -5 -2 输出: 20 做出这题的难度不是很大,至少很容易可以做到暴力求解,然而暴力求解的时间复杂度是很大的. 我用

排序算法习题汇总

1.冒泡排序 对于一个int数组,请编写一个冒泡排序算法,对数组元素排序. 给定一个int数组A及数组的大小n,请返回排序后的数组. 测试样例: [1,2,3,5,2,3],6 [1,2,2,3,3,5] class BubbleSort { public: int* bubbleSort(int* A, int n) { // write code here for(int i=0;i<n-1;i++){ int k=0; for(int j=0;j<n-i-1;j++){ if(A[j]&

算法习题---线性表之单链表逆序打印

一:题目 逆序打印单链表中的数据,假设指针指向单链表的开始结点 二:思路 1.可以使用递归方法,来进行数据打印 2.可以借助数组空间,获取长度,逆序打印数组 3.若是可以,对链表数据使用头插法,逆序排列,然后正序打印即可 三:算法实现(这里使用方法一:递归实现简单易懂) #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #define MAXSIZE 10 #define OK 1 #def

算法习题---线性表之单链表的查找

一:问题 已知一个带头结点的单链表,结点结构为(data,link)假设该链表只给出了头指针list,在不改变链表的前提下,设计一个尽可能高效的算法,查找链表中倒数第k个位置上的结点(k为正整数),若查找成功,算法输出该结点的data域的值,并返回1,否则,只返回0. 注意: 这里的链表中没有给出链表长度哟 二:思路 设置两个指针p.q,分别指向该链表的第一个元素(头结点的下一个元素)和头结点,一个整数num(初值为1),p向后移动一个位置num值加1,如果num值大于k,则p,q一起移动,p移

算法习题---线性表之数组实现循环移动

一:问题 设将n(n>1)个整数存放到一维数组R中,试设计一个在时间和空间两方面都尽可能高效的算法,将R中保存的序列循环左移p(0<p<n)个位置,即把R中的数据序列由(x0,x1,…,xn-1)变换为(xp,xp+1,…,xn-1,x0,x1,…,x). 二:思考 要实现R中序列循环左移P个位置,只需先将R中前P个元素逆置,再将剩下的元素逆置,最后将R中所有的元素再整体做一次逆置操作即可,本题算法描述如下: (一)步骤一:将前P个元素逆置 (二)步骤二:将后P个元素逆置 (三)步骤三:

算法习题---字符串的全排序列

一:什么是全排列 排列:从n个元素中任取m个元素,并按照一定的顺序进行排列,称为排列: 全排列:当n==m时,称为全排列: 比如:集合{ 1,2,3}的全排列为: { 1 2 3} { 1 3 2 } { 2 1 3 } { 2 3 1 } { 3 2 1 } { 3 1 2 } 我们可以将这个排列问题画成图形表示,即排列枚举树,比如下图为{1,2,3}的排列枚举树,此树和我们这里介绍的算法完全一致: 二:全排列实现思路 (1)n个元素的全排列=(n-1个元素的全排列)+(另一个元素作为前缀):