例題4-3--救濟金髮放
阿新 • • 發佈:2018-11-28
題目:
n(n<20)個人站成一圈,逆時針編號為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列。
解析:
注意題目的描述及示例,可以看出:要想經過1號,就要從n號開始逆時針走;要想經過n號,就要從1號開始順時針走;因此有:p1=n;p2=1;
圖示如下:
對於環狀問題,首先想到的就是求餘的方法,假設起始點為p,那麼每移動一個點的原始的寫法是:
p=(p+1)%n; //逆時針
p=(p-1)%n; //順時針
這樣寫有沒有問題呢?我們需要考慮邊界條件。 分為兩種情況:
1.當逆時針移動時,考慮到n對n求餘等於0的時候(因為圈內沒有為0的點),此時p=(n-1);對應上面的示範為n=9。我們希望下一個元素求餘等於10----這顯然是不可能的,因為一個數對n求餘,其結果x一定滿足:0<=x<=(n-1)
如何解決這個問題呢?不妨讓求餘結果為(n-1),最終結果再加上1,就可以達到n了。
其實也就是用當前的值減一再對n進行求餘,最終結果+1。
p=(p+d-1)%n+1; //其中d代表正一或負一(順時針或者逆時針走)
2.當順時針移動時,仍然考慮邊界條件。當p=1時,移動後的下一個值應該到了10 。可是帶入資料卻發現餘數為0 。回顧逆時針的情況,最終的結果可以由餘9加1得到,餘9顯然在p=n-1時成立,因此表示式為:
p=(p+d+n-1)%n+1;
觀察上面兩個表示式,發現他們的差別僅僅是d的符號不同–這可以用傳不同引數來改變,以及差了一個週期n–對於求餘來說,加減n的倍數最終的結果是不改變的。
程式碼:
#include<iostream> #include<Windows.h> #include<algorithm> #include<cstdio> #include<cstring> #include<ctype.h> #include<cmath> #define MAXN 10000 using namespace std; int n, k, m, a[MAXN]; int go(int p,int d,int t) { while (t--) { do { p = (p + d + n - 1) % n + 1; } while (a[p] == 0); //跳到非零的地方 } return p; } int main() { int i, j, p1, p2; int left; while (scanf("%d%d%d", &n, &k, &m) == 3 && n) { p1 = n;p2 = 1; left = n; for (i = 1;i <= n;i++) a[i] = i; while (left) { p1 = go(p1, +1, k); p2 = go(p2, -1, m); printf("%3d", p1); left--; if (p1 != p2) { printf("%3d", p2);left--; } a[p1] = a[p2] = 0; if (left) printf(","); } printf("\n"); } system("pause"); return 0; }