例題 4-3 救濟金髮放(The Dole Queue) UVa 133
阿新 • • 發佈:2018-12-26
題目:
為了縮短領救濟品的隊伍,NNGLRP決定了以下策略:每天所有來申請救濟品的人會被放在一個大圓圈,面朝裡面。選定一個人為編號 1 號,其他的就從那個人開始逆時針開始編號直到 N。一個官員一開始逆時針數,數 k 個申請者,然後另一個官員第 N 個始順時針方向數 m 個申請者,這兩個人就出圓圈。如果兩個官員數的是同一個人,那個人則出圈,如果選了兩個不同的人,則先輸出第一個第一個官員數出的那個人,然後2個官員再在剩下的人裡面繼續選直到沒人剩下來,注意兩個被選 中的人是同時走掉的,所以就有可能兩個官員選中一個人。
go函式每走一步都得到下一個位置 解析:至於下一個位置為什麼是p = (p+n+d)%n.其實很簡單。因為我們是一步步走的,所以只有兩種邊界情況。假設當前位置是p(0=<p<n),#include<stdio.h> #include<string.h> #define maxn 1000 int a[maxn]; int n, k, m; int go(int p, int d, int t) //巧妙 { while(t--){ do{ p = (p+d+n)%n; }while(a[p] == 0); } return p; } int main() { int people; while(scanf("%d%d%d",&n,&k,&m) == 3 && !(n == 0 && k == 0 && m == 0)){ for(int i = 0; i < n; i++ ){ a[i] = i+1; } int left = n;//剩餘 人數 int p1 = n-1, p2 = 0; // p指向起始處的前一個位置 while(left){ p1 = go(p1, 1, k); p2 = go(p2, -1, m); printf("%3d", p1+1);//注意 出隊人的序號比索引大 1 (陣列下標從零開始的) left--; if(p2 != p1){ printf("%3d",p2+1); left--; } a[p1] = a[p2] = 0; if(left) printf(","); } printf("\n"); } return 0; }
第一種邊界:p + 1 > n - 1,即 p + 1此時應該是到達0位置,但此時p + 1 = n,如果我們取餘數,則 (p+1)%T = 0,T = n(T表示這個圓圈的週期大小)。
剛好能符合,又因為T = n,所以(P+T+1)%T還是不變的。
第二種邊界: p - 1 < 0, 即 p - 1此時的值是-1,對於這種情況可以反過來看,它是向後退後1個單位,可以看成向前走T - 1個單位即p -1 等效於 p + T - 1
,我們要等到此時的位置,再去餘,(P+T-1)%T。
對於情況一、二。可以歸納為(P+T+d)%T,當為順時針是d取1,否則-1.