題意:同樣的約瑟夫問題,只是現在有K個好人,K個壞人,確定一個步長,是的在第一個好人被清除之前所有壞人都被清除乾淨。
題解:
1.壞人被清除掉的先後順序無關緊要,知道下一個除掉的是好人還是壞人就行了。2.由於好人一直都是k個,每除掉一個壞人,壞人數-1,所以隊列的總數每次-1但是好人一直是前k個3.下一個被清除的人在隊列中的“相對”序號為 s = (s+m-1)%rest4.只要s>=k,那麼下一個被除掉的就是壞人
5.接下來說說m的取值範圍:我們考察一下只剩下k+1個人時候情況,即壞人還有一個未被處決,那麼在這一輪中結束位置必定在最後一個壞人,那麼開始位置在哪呢?這就需要找K+2個人的結束位置,然而K+2個人的結束位置必定是第K+2個人或者第K+1個人,這樣就出現兩種順序情況:GGGG.....GGGXB 或 GGGG......GGGBX (X表示有K+2個人的那一輪退出的人)所以有K+1個人的那一輪的開始位置有兩種可能即第一個位置或K+1的那個位置,限定m有兩種可能:t(k+1) 或 t(k+1)+1; t>=1; 若遍曆每一個m必定逾時,避免逾時則需要打表和限制m的範圍。
#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>using namespace std;bool Josephus ( int k, int m ){ //處理的時候預設編號為0,1,2,···n-1; //每剔除一個人,就對剩下的所有人重新編一次號; //由於上一輪剔除了一個人,假設他的編號為t,那麼所有 > t 的編號都減小 1; //本來下一次報數應該從s+1開始,但是由於重新編號,所以現在從s開始,即s,s+1,s+2 ···s+(m-1) int n = k * 2, s = 0, rest; for ( int i = 1; i <= k; i++ ) { rest = n - i + 1; s = ( s + m - 1 ) % rest; if ( s < k ) return false; } return true;}int num[15] = { 0, 2, 7, 5, 30, 169, 441, 1872, 7632, 1740, 93313, 459901, 1358657, 2504881 };int main(){ int k, i; memset(num,0,sizeof(num)); while ( scanf("%d",&k) && k ) { if ( num[k] ) { printf("%d\n",num[k]); continue; } for ( i = k + 1; ; i += ( k + 1) ) { if ( Josephus ( k, i ) ) { num[k] = i; printf("%d\n",i); goto end; } else if ( Josephus ( k, i + 1 ) ) { num[k] = i + 1; printf("%d\n",i+1); goto end; } } end:; } return 0;}