1. 程式人生 > 實用技巧 >【洛谷P1145】約瑟夫

【洛谷P1145】約瑟夫

問題描述

n個人站成一圈,從某個人開始數數,每次數到m的人就被殺掉,然後下一個人重新開始數,直到最後只剩一個人。現在有一圈人,k個好人站在一起,k個壞人站在一起。從第一個好人開始數數。你要確定一個最小的m,使得在第一個好人被殺死前,k個壞人先被殺死

輸入格式

一行一個整數k。

輸出格式

一行一個整數m

樣例輸入

#1

3

#2

4

樣例輸出

#1

5

#2

30

資料範圍

0<k<14。

題解

最樸素的演算法就是一個一個數,用一個計數器記錄當前報的數,再用一個指標指向當前報數的人,如果當前位置上的人已經出局,就把指標往後移,直到找到第一個沒有出局的人。

報數是迴圈報的,即最後一個人報完後,下一個數由第一個人報,為了方便迴圈的實現,我們從0開始編號,這樣每次只要把指標加一再對總人數取模,就可以實現迴圈報數

考慮優化

我們只需要最後留下的都是好人,並且我們已經知道好人在前,壞人在後,這個相對位置是不會變的,而每個人最初的序號可以忽略

假設最初大家圍成一個大圓,每個人坐在一把椅子上,每次淘汰一個人就把那個人的椅子搬走,把圍成的圓縮小,再對每個人重新編號,這樣保證了每一次淘汰操作前所有人的編號是連續的

由於我們只要淘汰報m的人,我們可以以報m個數為一個迴圈,淘汰掉最後一個人。前面已經實現每一次淘汰前所有的人的編號是連續的,所以只要記錄上一次淘汰的人的位置,把這個位置加上m再對當前總人數取模就可以直接算出當前迴圈要淘汰的人

 1 #include <cstring>
 2 #include <cstdio>
 3
bool vis[50]; 4 int main() 5 { 6 int n,m,k,t,cnt,ok,i,j,num; 7 scanf("%d",&k); 8 n=k*2; 9 for (m=k;;m++) 10 { 11 t=0; ok=1; 12 for (j=1;j<=k;j++) 13 { 14 memset(vis,0,sizeof(vis)); 15 num=n-j+1; 16 if ((t+m)%num<k)
17 { 18 ok=0; 19 break; 20 } 21 t=(t+m)%num; 22 vis[t]=1; 23 } 24 if (ok) 25 { 26 printf("%d\n",m+1); 27 break; 28 } 29 } 30 return 0; 31 }