問題描述: 有k個壞人k個好人坐成一圈,前k個爲好人(編號1~k),後k個爲壞人(編號k+1~2k),現在從編號1開始報數,一直報到m,一輪結束後,必須要求第m個報數的人死掉,而且他要是壞人,在他死掉之後繼續從他的下一個開始報數,也是從1開始報到m,第m個是壞人,拉出去斃掉,直到壞人全部死去,好人全部留下,我們需要編程找到這個值m; 解題思路: 推導時要注意2點: 第一:每輪都是以前一輪死掉的人的後一個人作爲“1”開始順序編號的 如:k=3 編號:1 2 3 4 5 6 正確答案m=5 第一輪報數後,5號被殺掉,那麼以6號開始作爲下一輪的“1”重新編號,然後數到4,四號被殺掉,然後是6號被殺掉,結束!! 第二:f[i]=(f[i-1]+m)%(n-i); (i>1) 這是網上一些地方給出的遞推公式,對於本題而言是不正確的。因爲這種遞推公式針對
的是從0開始報數的Joseph,本題是從1開始報數的,必須要變形,最後就是由於本題k值有限,只有13個值,那麼POJ的數據測試就極有可能重複測試每個k值的結果,爲了節省總體時間,我們的程序只在第一次得到k值的時候計算m值,然後保存下來,當k值再次出現時,就直接把保存的結果輸出,不再計算m。這是在服務器打表的處理。另外有了遞推的程序後,我們就知道了每個k值對應的m值。此時追求0ms AC的同學可以利用遞推程序的結果,再寫一個程序,直接在程序裏面打表 int Joseph[]={0,2,7,5,30,169,441,1872,7632,1740,93313,459901,1358657,2504881,1245064}; <此題有參考某同學的······>#include<iostream> using namespace std; int main(void) { int Joseph[14]={0}; //打表,保存各個k值對應的m值 int k; while(cin>>k) { if(!k) break; if(Joseph[k]) { cout<<Joseph[k]<<endl; continue; } int n=2*k; int ans[30]={0}; //第i輪殺掉 對應當前輪的編號爲ans[i]的人,每一輪都以報數爲“1”的人開始重新編號 int m=1; //所求的最少的報數 for(int i=1;i<=k;i++) //輪數 { ans[i]=(ans[i-1]+m-1)%(n-i+1); //n-i爲剩餘的人數 if(ans[i]<k) //把好人殺掉了,m值不是所求 { i=0; m++; //枚舉m值 } } Joseph[k]=m; cout<<m<<endl; } return 0; }
POJ之1012
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.