PTA例題精析-約瑟夫問題 Josephus Problem

0 問題描述

【人數(shù)為n,步長為m】有n個(gè)人匿沛,編號為1~n逃呼,從第一個(gè)人開始報(bào)數(shù)者娱,從1開始報(bào)苏揣,報(bào)到m的人會(huì)死掉,然后從第m+1個(gè)人開始框沟,重復(fù)以上過程增炭。問最后一個(gè)人的編號是隙姿?

猴子選大王

【人數(shù)為n,步長為3】一群猴子要選新猴王哎甲。新猴王的選擇方法是:讓N只候選猴子圍成一圈饲嗽,從某位置起順序編號為1~N號。從第1號開始報(bào)數(shù)吞加,每輪從1報(bào)到3尽狠,凡報(bào)到3的猴子即退出圈子,接著又從緊鄰的下一只猴子開始同樣的報(bào)數(shù)践图。如此不斷循環(huán),最后剩下的一只猴子就選為猴王沉馆。請問是原來第幾號猴子當(dāng)選猴王码党?正整數(shù)N(≤1000)

1 數(shù)組模擬,m=3

這里使用數(shù)組暴力模擬的問題在于斥黑,已經(jīng)被淘汰的猴子仍然會(huì)被循環(huán)到

#include <stdio.h>
int a[1001];// 1表示淘汰 
int main(){
    int n = 0,s=0,count=0;
    int i = 0; // 數(shù)組下標(biāo) 
    scanf("%d", &n);
    while(s != n-1){ // 淘汰猴子的數(shù)量達(dá)到n-1時(shí)退出
        i++;
        if (i>n){
            i = 1;
        }
        if(a[i] == 0){ // 只判斷剩下的猴子 
            count++;
            if (count %3==0){
                a[i] = 1;
                s++; // 每次淘汰一只猴子揖盘,直到s==n-1,剩下最后一只 
            } 
        } 
        
    }
    for (int j=1;j<=n;j++){
        if (a[j]==0){
            printf("%d",j);
        }
    }
    return(0);
}

2 鏈表模擬锌奴,m=3

image.png

鏈表方法很好理解兽狭,首尾相連成環(huán),n個(gè)人中會(huì)淘汰n-1個(gè)人,所以外層循環(huán)是n-1箕慧,鏈表使用curr = curr->Next 2次服球,那么颠焦,就找到了數(shù)3的人有咨,把它從鏈表中刪除,這次外層循環(huán)就走完了蒸健,剩下n-2個(gè)人。

這里使用了單向鏈表婉商,用一個(gè)last指向了出圈者的上一個(gè)人似忧,以便于對出圈者進(jìn)行鏈表刪除操作。也可以考慮使用雙向鏈表丈秩。時(shí)間復(fù)雜度O(nm)

#include <stdio.h>
#include <stdlib.h>

typedef struct Node* Link;
struct Node{
    int Num;
    Link Next;
};
int main(){
    int n=0;
    scanf("%d", &n);
    Link head = (Link) malloc(sizeof(struct Node));
    head->Num = 1;
    head->Next = NULL;
    Link curr = head;
    for (int i=2;i<=n;i++){
        Link node = (Link) malloc(sizeof(struct Node));
        node->Num = i;
        node->Next = NULL;
        curr->Next = node;
        curr = curr->Next;
    }
    curr->Next = head;
    Link last = curr;
    curr = head;

    // operate
    for (int i=1;i<n;i++){
        for (int j=1;j<3;j++){
            last = curr;
            curr = curr->Next;
        }
        printf("出圈:%d\n",curr->Num);
        Link Temp = curr;
        last->Next = curr->Next;
        free(Temp);
        curr = last->Next;
    }
    printf("勝利者:%d\n",curr->Num);
    return(0);
}

3 遞歸法盯捌,m=3

使用遞歸求解問題需要找到三個(gè)關(guān)鍵點(diǎn),從而找到對應(yīng)的遞推公式蘑秽。

  1. 該問題的解可以分解為幾個(gè)子問題饺著?
  2. 確保問題本身和子問題,除了數(shù)據(jù)規(guī)模不同肠牲,求解思路完全一樣
  3. 確定遞歸終止條件

待著上述3個(gè)關(guān)鍵點(diǎn)幼衰,我們來看約瑟芬問題如何用遞歸求解。

我們從0開始做下標(biāo)缀雳。
設(shè)f(n,m)表示n個(gè)人數(shù)m時(shí)問題的解渡嚣,那么f(n-1,m)是n-1個(gè)人數(shù)m時(shí)問題的解。
顯然我們知道不管m等于多少f(1) = 0肥印。
假設(shè)f(n-1)的答案(勝利者的下標(biāo))已經(jīng)求出识椰,那么思考一下f(n-1)的勝利者的下標(biāo)位置和f(n)勝利者的下標(biāo)位置之間有什么關(guān)系呢?

image.png

圖中最上方的兩行黃色深碱,列出了n從1~11時(shí)腹鹉,對應(yīng)的f(n,3)結(jié)果。

  • 下方的圖拿n=11個(gè)人編號0~10敷硅,第一次數(shù)的時(shí)候從0數(shù)到2數(shù)了3個(gè)數(shù),那么2就被踢出去了功咒,標(biāo)記為紅色,之后永遠(yuǎn)不可能數(shù)到2了绞蹦,下方設(shè)置為灰色航瞭。剩下10個(gè)人。
  • 剩下10個(gè)數(shù)編號0~9坦辟,編號為3的人是這10個(gè)人隊(duì)伍的頭刊侯,從0開始重新報(bào)數(shù)。仍然是報(bào)到的人被踢出去锉走,剩下9個(gè)人0~8滨彻。

所以我們可以看到f(n)和f(n-1)下標(biāo)之間有相差為m的關(guān)系藕届。
f(k) = ( f(k-1)+3 )%k ,效率O(n)
理解這個(gè)遞推式的核心在于

  • 關(guān)注勝利者的下標(biāo)位置是怎么變的。
  • 每次去掉一個(gè)人以后亭饵,下一個(gè)報(bào)數(shù)的人從0開始編號休偶。

如果下一個(gè)報(bào)數(shù)的人,不從0開始編號會(huì)怎么樣呢辜羊?如下圖看到踏兜,n=11的時(shí)候,f(11,3)是從0開始報(bào)數(shù)的八秃,到編號2的時(shí)候被踢出去碱妆,剩下10人。
10人中昔驱,編號為3的人是這10個(gè)人隊(duì)伍的頭疹尾,但是我們給他編號9。他仍然是第一個(gè)報(bào)數(shù)骤肛,報(bào)了第三個(gè)數(shù)的人(編號1)將被踢出去纳本。對于這10個(gè)人來說,是從最后一個(gè)編號開始報(bào)數(shù)的腋颠,這與題意不符合繁成,所以它的值根本不能表示f(10,3)

image.png

分析了這么多淑玫,我們發(fā)現(xiàn):

  1. f(n,m) 可以分解成1個(gè)子問題f(n-1,m)
  2. f(n,m)的求解思路和f(n-1,m)一樣朴艰,只是數(shù)據(jù)規(guī)模不同
  3. 遞歸終止條件為f(1)=0
  4. 遞推公式為 f(n,m) = (f(n-1,m) + m)%n , f(1)=0

最后,題目要求下標(biāo)從1開始混移,所以返回ans+1

#include <stdio.h>

int main(){
    int n = 0,m=3;
    scanf("%d", &n);
    int ans = 0;
    for (int i=1;i<=n;i++){
        ans = (ans + m)%i;
    }
    printf("%d", ans+1);
    return(0);
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末祠墅,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子歌径,更是在濱河造成了極大的恐慌毁嗦,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件回铛,死亡現(xiàn)場離奇詭異狗准,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)茵肃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門腔长,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人验残,你說我怎么就攤上這事捞附。” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵鸟召,是天一觀的道長胆绊。 經(jīng)常有香客問我,道長欧募,這世上最難降的妖魔是什么压状? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮跟继,結(jié)果婚禮上种冬,老公的妹妹穿的比我還像新娘。我一直安慰自己舔糖,他們只是感情好娱两,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著剩盒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪慨蛙。 梳的紋絲不亂的頭發(fā)上辽聊,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機(jī)與錄音期贫,去河邊找鬼跟匆。 笑死,一個(gè)胖子當(dāng)著我的面吹牛通砍,可吹牛的內(nèi)容都是我干的玛臂。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼封孙,長吁一口氣:“原來是場噩夢啊……” “哼迹冤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起虎忌,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤泡徙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后膜蠢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體堪藐,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年挑围,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了礁竞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,722評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡杉辙,死狀恐怖模捂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤枫绅,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布泉孩,位于F島的核電站,受9級特大地震影響并淋,放射性物質(zhì)發(fā)生泄漏寓搬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一县耽、第九天 我趴在偏房一處隱蔽的房頂上張望句喷。 院中可真熱鬧,春花似錦兔毙、人聲如沸唾琼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锡溯。三九已至,卻和暖如春哑姚,著一層夾襖步出監(jiān)牢的瞬間祭饭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工叙量, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留倡蝙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓绞佩,卻偏偏與公主長得像寺鸥,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子品山,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評論 2 353