看起来简单,实际上有很多东西可以仔细咀嚼!
Description
In a serious attempt to downsize (reduce) the dole queue, The New National Green Labour Rhinoceros Party has decided on the following strategy. Every day all dole applicants will be placed in a large circle, facing inwards. Someone is arbitrarily chosen as number 1, and the rest are numbered counter-clockwise up to N (who will be standing on 1’s left). Starting from 1 and moving counter-clockwise, one labour official counts off k applicants, while another official starts from N and moves clockwise, counting m applicants. The two who are chosen are then sent off for retraining; if both officials pick the same person she (he) is sent off to become a politician. Each official then starts counting again at the next available person and the process continues until no-one is left. Note that the two victims (sorry, trainees) leave the ring simultaneously, so it is possible for one official to count a person already selected by the other official.
Input
Write a program that will successively read in (in that order) the three numbers (N, k and m; k, m > 0, 0 < N < 20) and determine the order in which the applicants are sent off for retraining. Each set of three numbers will be on a separate line and the end of data will be signalled by three zeroes (0 0 0).
Output
For each triplet, output a single line of numbers specifying the order in which people are chosen. Each number should be in a field of 3 characters. For pairs of numbers list the person chosen by the counter-clockwise official first. Separate successive pairs (or singletons) by commas (but there should not be a trailing comma).
Samples
Input
10 4 3
0 0 0
Output
4 8, 9 5, 3 1, 2 6, 10, 7
前言
这个题真的太妙了
我愿称之为代码的艺术
题目大意
现在有n个人围成一个圈,然后有两名管理者分别从第一个人 逆时针 开始点人,从第n个人顺时针开始点人。逆时针的管理者每次将数到的第k个人移除圆圈队列,顺时针的管理者每次将数到的第m个人移除圆圈队列 。然后继续从当前空位置开始重复上述操作,一直这样操作下去,直到所有人都被移除圆圈队列,求出队列中人员离开的顺序。
样例解释
第一轮:逆时针 空→1→2→3→4
顺时针:空→10→9→8
(4,8被移除)
第二轮:逆时针:4→5→6→7→8(已经被移除了)→9
顺时针:8→7→6→5
(9,5被移除)
第三轮:逆时针:9→10→1→2→3
顺时针:5→4(已被移除)→3→2→1
(3,1被移除)
第四轮: 3→4(移除)→5(移除)→6→7→8(移除)→9(移除)→10→1(移除)→2;
顺时针 :1→10→9(移除)→8(移除)→7→6
(2,6被移除)
。。。。。。。。略。。。。。。。。。。
题目注意事项及小技巧
1.循环节的处理:正数的循环节可以直接用%进行操作,那么负数怎么办?(因为顺时针逆时针的区分,一定有一个要利用负数循环操作)
负数的循环取余可以通过 ( +n)%n的方法来进行操作!
2.多组输入的处理:
以0 0 0末尾结束,如果是以单0结尾,可以 &&n这样操作
,如果0 0 0结尾,可以 &&( n || k| | m) 这样操作
3.注意每次的标记是选完两个人之后再进行标记,让两人离开
而不是选一个标记一个,这里要搞清楚。
4.注意第一次选人的时候,是算上1 和 10 本身的,但是后续的选择并没有算上起点本身,这里要搞清楚!所以我们在循环查找的时候,我们的起点必须提前一个数字!(提前一位循环起点)!
5.循环结束条件:因为题目要求再所有人都离开之后结束,不如
就设一个数字t=n,每离开一个人t- -,最后 t=0时结束循环!
#include<bits/stdc++.h>
using namespace std;
int vis[100001];
int n;
int go(int start, int d, int step )//本题核心;用一个函数概括了顺时针和逆时针的情况!
{
for ( int i = 0; i < step; i ++ )
{
do//这个do while循环,保证每次一定是有效步数,走一次检测一次的态度!
//这个地方是本题的核心:一个走k步和m步,保证走的步数一定是有效步数
//原思路:先走k步,如果不满足(判断),再往下一步一步走!直到找到一个满足的!
//而且这样也不能保证之前走的步一定是有效的,直接麻了!
//而且没有概括顺走和逆走不过就是一个+1-1的区别!
{
start = ( start+d+n )%n;//满足环状结构!,负数的环形结构:太妙了,+n!
}while ( vis[start] );
}
return start;//0
}
int main()
{
int k,m;
while(cin>>n>>k>>m&&(n||k||m))//这里如果结尾是0 0 0结尾,而不是一个0结尾,可以这样写!
{
memset(vis,0,sizeof(vis));
int t=n;
int ni=n-1;//
int shun=0;
//这个题真的太叼了!
while(t)//这里可以不用flag盲目结束循环了!
{
ni=go(ni,1,k);
shun=go(shun,-1,m);
printf("%3d",ni+1);
t--;//每输出一个就--,知道所有的数字都被标记,这样就不用盲目的flag了!
if(shun!=ni)
{
printf("%3d",shun+1);//这里的+1太妙了,如果没有这个+1,10这个数字根本输出不来!
t--;
}//输出问题:这个输出真的太诡异了:最后直接用 3d控制一下!
vis[shun]=vis[ni]=1;//写完之后再进行两次标记!
if (t)
printf (",");//这个地方写的真妙啊!
}
printf("\n");
}
return 0;
}
关于代码的分析总结(自我总结)
以下是我个人做题时遇到的一些问题总结
1.是否合法:我原本的思路是让当前位置先逆时针走k步,然后判断是否合法,如果不合法再进行while循环查找有效位置。
但是这样太麻烦了,而且前面k步也不能保证有效合法。
所以这里直接用一个do while循环保证走的每一步一定是合法!
2.函数思想:找到顺时针查找,和逆时针查找的规律,原来他们俩其实就是一个+1-1的差别,如果把两个查找分开写,真的是乱死了,所以这边写了一个函数,用形式参数d的正负来控制顺时针和逆时针。这样要简便的多!
3.循环思想:因为%的原因,在%循环查找时一定会出现一个0,但是我们很痛苦的就是,这是一个1-n的序列,并不会有0,所以我们只能用0表示n,
但是新的问题又出现了 输出的时候,我们的n会在输出的时候也输出0,为了解决这个问题,我研究出两种方法
1.正常循环,在结果=0的情况下特判输出n!
2.再将循环起点提前一个位置(之前为了满足查找时不算本身的条件时已经提前一位了):然后最后输出的时候对结果进行+1!
*上面的代码用的是第二种
题目不难,但是用函数来概括顺时针和逆时针的情况太美了
刚开始做题的时候没有想到,这就是一个大错误!
这样的函数思路再平时做题或者比赛可以很好的让我的代码
不至于乱掉,保持我的思路更加清晰!
2021/5/22.