题海拾贝——救济金发放(The Dole Queue, UVa 133)

        Hello大家好!很高兴我们又见面啦!给生活添点passion,开始今天的编程之路!

23929f95d55d47d5a0afdbd25b2740f5.gif

我的博客:<但凡.

我的专栏:《题海拾贝》《编程之路》

欢迎点赞,关注!

目录

1、题目

2、分析

3、题解(附详细解析)

4、拓展


 

1、题目

        n(n个人站成一圈,逆时针编号为 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 列。


2、分析

        我们可以让A和B开始选人,选到的人打印出来并在数组中的位置改为0,当下一次找人的时候跳过这个为0的位置。我们可以定义一个剩余的人的变量,这样当检测到剩余的人为0时结束程序。


3、题解(附详细解析)

注:输出结果和实现方式可能与原题有略微不同。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int arr[100] = { 0 };
int go(int n, int start, int d, int T)
{
	while (T--)//一次走一步,走T次
	{
		//des = (start + d)%n;有可能下标为0,但是我们设置第一个人的下标为1
		do {
			start = (start + d + n - 1) % n + 1;//这样写可以让标号为0表示为标号为10
		} while (arr[start] == 0);//如果该位置为0,则重复执行上一步操作
	}
	return start;
}
int main()
{
	int n = 0;//总人数
	int k = 0;
	int m = 0;
	int i = 0;
	//A从1逆时针开始数,B从n开始顺时针数
	scanf("%d %d %d", &n, &k, &m);//scanf的返回值为输入数据的个数
	for (i = 1;i <= n;i++)//给予每个人标号,但注意第一个人标号为1,不是0
	{
		arr[i] = i;
	}
	int left = n;//剩余人数,注意left=n要在n不等于0时给
	//go函数传入总人数起始位置,步长(这里使用步长目的是合并逆时针和顺时针到一个函数),走的步数
	int startA = n;//注意,我们数的时候算着第一个人,所以我们相当于是从n作为起始位置
	int startB = 1;
	while (left)
	{
		//1为逆时针数,-1为顺时针数
		startA = go(n, startA, 1, k);//返回挑选的人的标号
		startB = go(n, startB, -1, m);//返回挑选的人的标号
		if (startA != startB)
		{
			printf("%3d%3d ", startA, startB);
			left -= 2;
		}
		else
		{
			printf("%3d", startA);//挑选的人相等时只打印一个
			left--;
		}
		arr[startA] = 0;//给挑选过的人赋值为0
		arr[startB] = 0;
		if (left)
		{
			printf(",");
		}
	}

	return 0;
}

4、拓展

        其实在写这个题时我一直做不对,因为我把题中的逆时针编号看成顺时针编号了。。。

        那么顺时针编号的情况下怎么实现呢?

        实际上我们只需要更改两个地方就可以了。首先把A和B的起始位置更改一下,其次把传参时1和-1改一下。因为我们要1始终对应的是序号变大的方向,所以顺时针时1代表顺时针转,所以我们给A的go函数参数里为-1,B为1,实现代码:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int arr[100] = { 0 };
int go(int n, int start, int d, int T)
{
	while (T--)//一次走一步,走T次
	{
		//des = (start + d)%n;有可能下标为0,但是我们设置第一个人的下标为1
		do {
			start = (start + d + n - 1) % n + 1;//这样写可以让标号为0表示为标号为10
		} while (arr[start] == 0);//如果该位置为0,则重复执行上一步操作
	}
	return start;
}
int main()
{
	int n = 0;//总人数
	int k = 0;
	int m = 0;
	int i = 0;
	//A从1逆时针开始数,B从n开始顺时针数
	scanf("%d %d %d", &n, &k, &m);//scanf的返回值为输入数据的个数
	for (i = 1;i <= n;i++)//给予每个人标号,但注意第一个人标号为1,不是0
	{
		arr[i] = i;
	}
	int left = n;//剩余人数,注意left=n要在n不等于0时给
	//go函数传入总人数起始位置,步长(这里使用步长目的是合并逆时针和顺时针到一个函数),走的步数
	int startA = 1;//注意,我们数的时候算着第一个人,所以我们相当于是从n作为起始位置
	int startB = n;
	while (left)
	{
		startA = go(n, startA, -1, k);//返回挑选的人的标号
		startB = go(n, startB, 1, m);//返回挑选的人的标号
		if (startA != startB)
		{
			printf("%3d%3d ", startA, startB);
			left -= 2;
		}
		else
		{
			printf("%3d", startA);//挑选的人相等时只打印一个
			left--;
		}
		arr[startA] = 0;//给挑选过的人赋值为0
		arr[startB] = 0;
		if (left)
		{
			printf(",");
		}
	}

	return 0;
}

        另外我们可以运用上面这道题的思想再来一道题:

        体育课上有n个人(从1到n标号)站成一排,体育老师让他们从1开始报数,每个从报到m的人出列,最后只剩下一个人,请打印出这个人的标号。

实现代码:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
    int i = 0;
    int n = 0;
    int m = 0;
    scanf("%d %d", &n, &m);
    int arr[100] = { 0 };
    for (i = 1; i <= n; i++)
    {
        arr[i] = i;
    }
    int T = n -1;
    i = 0;
    while(T)
    {
        int k = m;
        while (k)
        {
             i = (i + 1-1) % n+1;
            while (arr[i] == 0)
            {
                i = (i + 1 - 1) % n + 1;
            }
            k--;
        }
        arr[i] = 0;
        T--;
    }
    for (i = 0;i < n+1;i++)
    {
        if(arr[i]!=0)
        {
            printf("%d", arr[i]);
        }
    }
    return 0;
}

        好了,今天的内容就分享到这,我们下期再见!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值