参考:
https://matiji.net/exam/brushquestion/19/3181/1DC60EA6DF83A333301CFFE1407FBA59
https://www.bilibili.com/video/BV1ss4y1Y7gB/?t=1065.1
问题引入
马上要进行阅兵仪式了,将军要在手下 n 个人中挑人参加,他用以下方法选人:
n 个人站成一圈,逆时针编号为 1−n ,现在 A、B 两人选人。A 逆时针数 k 个停下来,B 顺时针数 m 个停下来,被选中的1或2个人输出,直至所有人输出。
首次从 A 从1开始, B 从 N 开始,之后 AB 分别从上次各自的位置的各自方向的下一位接着数。结合样例1可知:
请自行推理
输入格式:
可能有多组数据,每组一行输入三个整数n、k、m,输入0 0 0 停止。
输出格式:
输出每轮选出的人,用,隔开,每个编号占3个格子(右对齐)。
输入:
10 4 3
0 0 0
输出:
4 8, 9 5, 3 1, 2 6, 10, 7
备注:
0<n≤20;k、m在int范围内,且为正数;组数少于20。
问题分析
圈圈编号循环问题。我采用的是双循环的办法,就是直接将题目翻译一遍,比较直白。但是编号的办法太笨了。写了80多行代码才ac。
题解代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| #include<bits/stdc++.h> using namespace std;
int ans(int nn[30],int n,int k,int m);
int main( ) { int n,k,m; cin >> n >> k >> m; while(n!=0 && k!=0 && m!=0) { int nn[30]={0}; ans(nn,n,k,m); cin >> n >> k >> m; } return 0; }
int ans(int nn[30],int n,int k,int m) { int a=0,b=1,total=n; int aa=0,bb=0; for(int i=1;i<=n;++i) { nn[i]=1; } while(total>0) { while(aa<k) { a=(a+1)%(n+1); if(nn[a]==1) { aa++; } } aa=0; while(bb<m) { b=(b-1)%(-n); if(nn[n+b]==1) { bb++; } } bb=0; if(a==n+b) { total--; if(total==0) { printf("%3d\n",a); } else { printf("%3d,",a); } } else { total-=2; if(total==0) { printf("%3d%3d\n",a,n+b); } else { printf("%3d%3d,",a,n+b); } } nn[a]=0; nn[n+b]=0; } return 0; }
|
改进
看了up主轩哥码题的解题思路,学到了一个重要知识点:
通常用静态数组加下标的方式模拟这种循环结构。
任意的起始点到任意的终点,用下标进行循环。
假设数组 A ,起始位置为 k ,当前的位置为 p ,每一次的步长为 d (其中 d 有两种可能,1和-1 。1表示正循环,-1表示逆循环),循环体周期的长度为 T 。
由此得到下标的循环公式:
(p−k+d+T)%T+k
给上代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| #include<bits/stdc++.h> using namespace std;
int n,k,m; bool a[25];
int pick(int p,int d,int c) { while(c--) { do{ p=(p-1+d+n)%n+1; }while(a[p]==0); } return p; }
int main() { while(cin >> n >> k >> m,!(n==0 && k==0 && m==0)) { for(int i=1;i<=n;++i) { a[i]=true; } int remain=n; int pa=n,pb=1; while(remain) { pa=pick(pa,1,k); pb=pick(pb,-1,m); if(pa==pb) { printf("%3d",pa); remain--; } else { printf("%3d%3d",pa,pb); remain-=2; } a[pa]=a[pb]=0;
if(remain) printf(","); } printf("\n"); } return 0; }
|
OvO