•参考资料
[1]:紫书P82
•题意(by紫书)
按照被选中的次序输出这 n 个人的编号;
如果A和B选中的是同一个人,输出一个这个人的编号;
输出格式:输出的每个编号占3个字节,不够3个字节在前面用空格补;
•循环报数处理技巧
n个人按照逆时针顺序编号1~n;
给你一个整数 k 和 cur;
cur表示从这 n 个人中任意选取的一个编号;
k > 0 : 找 cur 左手边的第 k 个人的编号;
k < 0 : 找 cur 右手边的第 k 个人的编号;
循环报数问题,需要处理的边界问题是:
编号 1 的左手边的人的编号为 n;
编号 n 的右手边的人的编号为 1;
之前常用的处理的方式为,循环处理,如果 cur 从编号 n 来到编号 n+1,特判,令其等于 1;
反之,如果 cur 从编号 1 来到编号 0,特判,令其等于 n;
下面说下一我从紫书上学到的技巧;
1 pos = (cur + k - 1 + n)%n + 1; 2 pos : 从cur编号顺时针或逆时针找到的第k个人的编号 3 k : k > 0,找cur右手边的第k个人的编号,反之找cur左手边的第k个人的编号;1 while(~scanf("%d%d%d",&n,&cur,&k)) 2 { 3 k=k%n; 4 int pos=(cur+k-1+n)%n+1; 5 cout<<pos<<endl; 6 }•我的理解
n个人顺时针围城一圈,从 x 位置开始,顺时针找其左(或逆时针找其右)手边的第 k 个人(-n < k < n , k > 0 顺时针找, k < 0 逆时针找);
假设 k > 0 ,那么第 k 个人的编号为:
合并这两个式子就是
nextPos = (x+k-1+n)%n+1;
简单证明这个式子得正确性:
①如果 x+k ≤ n,那么 (x+k-1+n)%n+1 = x+k;
②如果 x+k > n:
1)x+k = n+1 : (x+k-1+n)%n+1 = 1;
2)x+k > n+1 : (x+k-1+n)%n 就是目的编号的前一个编号,+1就等于目的编号;
那如果 k < 0 呢?
假设找 x 左手边的第 y 个人的编号 = 找 x 右手边的第 k 个人的编号;
那么 |k| + y = n,也就是 y = n-|k|;
带入上式得:
nextPos = (x+n-|k|-1+n)%n+1;
即 nextPos = (x-|k|-1+n)%n+1;
综上,不论 k 是大于0还是小于0,nextPos = (x+k-1+n)%n+1;
•Code
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define memF(a,b,n) for(int i=0;i <= n;a[i++]=b); 4 5 int n,k,m; 6 bool vis[30]; 7 8 int Go(int cur,int d,int x) 9 { 10 /** 11 d=1:逆时针找第x个人 12 d=-1:顺时针找第x个人 13 当前的cur肯定是不满足条件的位置 14 所以初始 a=n,b=1 15 之后,a,b的值就是上一次出队的编号 16 */ 17 while(x--) 18 { 19 do 20 { 21 cur=(cur+d-1+n)%n+1; 22 }while(vis[cur]); 23 } 24 return cur; 25 } 26 void Solve() 27 { 28 memF(vis,false,n); 29 30 int a=n,b=1; 31 int left=n; 32 while(left--) 33 { 34 a=Go(a,1,k); 35 b=Go(b,-1,m); 36 37 vis[a]=true; 38 vis[b]=true; 39 40 printf("%3d",a); 41 if(b != a) 42 { 43 left--; 44 printf("%3d",b); 45 } 46 if(left) 47 printf(","); 48 } 49 printf(" "); 50 } 51 int main() 52 { 53 while(~scanf("%d%d%d",&n,&k,&m) && n+k+m) 54 Solve(); 55 56 return 0; 57 }