2018浙江理工大學程序設計校賽

19校賽快到了 我這個菜雞連去年的題目都還沒看過 現在趕緊來惡補一下(基於搜索引擎做題) 可惜的是今年新生賽我沒法參加了 要去南京參加機器人比賽
有一說一 獎狀和獎金好誘人啊
PS:本文持續更新 就看我啥時候能寫完了

Probelm K:第k人

題目描述

TT在跟朋友一起做遊戲,
遊戲規則:n個小朋友(從1開始編號)手拉手,從第一個小朋友開始報數,喊出k的小朋友從圈中出去,然後下個小朋友從1開始報數。
當圈中只剩下一個小朋友的時候結束。TT想知道誰會勝出。

輸入

T(T組,T<=10)
T行,每行兩個個數字n,k代表當前有n個小朋友(n<=1000)k如上述(k<=1e5)

輸出

對每組輸出勝出者的編號

樣例輸入

1
5 2

樣例輸出

3

代碼實現

#include<iostream>
using namespace std;
int main()
{
    int t,n,i,k,f;
    while(cin>>t)
    {
        while(t--)
        {
            cin>>n>>k;
            for(i=1;i<=n;i++)   f=(f+k)%i;
            cout<<f+1<<endl;
        }
    }
    return 0;
}

Probelm L:新第k人

題目描述

TT在跟朋友一起做遊戲,
遊戲規則:n個小朋友(從1開始編號)手拉手,從第一個小朋友開始報數,喊出k的小朋友從圈中出去,然後下個小朋友從1開始報數。當圈中只剩下一個小朋友的時候結束。
TT特別喜歡233這個數字,他把遊戲修改爲第233人。即報出233的人要退出遊戲。
由於第233人太好玩了,玩的人越來越多,現在你還會知道誰會勝出嗎?

輸入

T(T組,T<=10000)
T行,每行一個數字n代表當前有n個小朋友(n<=10000);

輸出

對每組輸出勝出者的編號

樣例輸入

1
5

樣例輸出

5

代碼實現

只要把上一題的k改成233就對了

#include<iostream>
using namespace std;
int main()
{
    int t,n,i,f;
    while(cin>>t)
    {
        while(t--)
        {
            cin>>n;
            for(i=1;i<=n;i++)   f=(f+233)%i;
            cout<<f+1<<endl;
        }
    }
    return 0;
}

知識點:K和L都是約瑟夫環
PS:直接暴力據說是在一定優化後有一定的可能會AC,但是我沒成功。
關於約瑟夫環 百度百科就有解釋 代碼是 f=(f+k)%m 具體解析我看着百度那個看了一晚上愣是沒算明白 最後找到一篇比較好的博客

約瑟夫環
約瑟夫環是一個數學的應用問題:已知n個人(以編號1,2,3…n分別表示)圍坐在一張圓桌周圍;從編號爲k的人開始報數,數到m的那個人出列;他的下一個人又從1開始報數,數到m的那個人又出列;依此規律重複下去,直到圓桌周圍的人全部出列。

前幾天,在一篇文章中得知了約瑟夫環的問題。然後,就涉及瞭解決辦法。這個問題,在許多計算機或者關於數據結構的書中都有提及,而其中的解決辦法便是使用循環鏈表——無論這個循環鏈表是使用指針還是數組實現,模擬約瑟夫環的進行,最後得到解決方案。具體方法肯定早有某人披露。但是,令人深感奇妙的還是這個問題的數學解決。在不統計解決過程,即不統計每次都需要出列哪個序號時,就可以應用數學遞推公式,直接得出最後剩下的那個人的序號。

使用數學方式,奇妙是很奇妙,也確實讓人感受到數學的魅力。可是,在我查找如何推導出這個公式的時候,網上的解決方案都是在我覺得很關鍵的地方一筆帶過。“衆所周知”,“顯而易見”,“很簡單的”……諸如此類。我悲劇的鬱悶了。所以在大家都陶醉在“數學真奇妙啊”的飄飄然中時,我惴惴不安。因爲我只感受到了數學帶給我的困惑。

對着網上的推導思考良久,終於茅塞頓開。所以趕緊寫下來,以防以後再次悲劇和鬱悶。並且希望以後看到這篇文章的人,能和我一樣擺脫困惑。

先總結一下約瑟夫環的遞推公式:

f[1]=0; f[i]=(f[i-1]+m)%i; (i>1)

f[1]=1; f[i]=(f[i-1]+m)%i  (i>1);   if(f[i]==0) f[i]=i;

P(1, m, k)=1 (i = 1);   P(i, m, k)=[P(i - 1, m, k ) + m - 1] % i + 1 (i > 1, 此處先減1是爲了讓模i的值不爲0)

那麼這三個公式有什麼不同?

首先可以肯定的是這三個公式都正確。公式1,得到的是以0~n-1標註的最終序號;公式2,3得到的就是正常的1~n的序號;並且公式2和公式3其實是一個意思。下面我們就分別推導三個公式,並且推導的過程中,你也就能明白這三個公式的共同點和不同點。

公式1的推導:——————————

給出一個序列,從0~n-1編號。其中,k代表出列的序號的下一個,即k-1出列。

a 0, 1, …, k-1, k, k+1, …, n-1

那麼,出列的序號是(m-1)%n,k=m%n(這個可真的是顯而易見)。出列k-1後,序列變爲

b 0, 1, …, k-2, k, k+1, …, n-1

然後,我們繼續從n-1後延長這個序列,可以得到

c` 0, 1, …, k-2, k, k+1, …, n-1, n, n+1, …, n+k-2

我們取從k開始直到n+k-2這段序列。其實這段序列可以看作將序列b的0~k-2段移到了b序列的後面。這樣,得到一個新的序列

c k, k+1, …, n-1, n, n+1, …, n+k-2

好了,整個序列c都減除一個k,得到

d 0, 1, …, n-2

c序列中的n-1, n, n+1都減除個k是什麼?這個不需要關心,反正c序列是連續的,我們知道了頭和尾,就能知道d序列是什麼樣的。

這樣你看,從序列a到序列d,就是一個n序列到n-1序列的變化,約瑟夫環可以通過遞推來獲得最終結果。ok,繼續向下。

剩下的就是根據n-1序列遞推到n序列。假設在n-1序列中,也就是序列d中,我們知道了最終剩下的一個序號是x,那麼如果知道了x轉換到序列a中的編號x`,不就是知道了最終的結果了麼?

下面我們就開始推導出序列a中x的序號是什麼。

d->c,這個變換很容易,就是x+k;

c->b,這個變換是網上大家都一帶而過的,也是令我鬱悶的一個關鍵點。從b->c,其實就是0~k-2這段序列轉換爲n~n+k-2這段序列,那麼再翻轉回去,簡單的就是%n,即(x+k)%n。%n以後,k~n-1這段序列值不會發生變化,而n~n+k-2這段序列則變成了0~k-2;這兩段序列合起來,就是序列b。

於是乎,我們就知道了,x`=(x+k)%n。並且,k=m%n,所以x`=(x+m%n)%n=(x+m)%n。公式1就出來了:f[i]=(f[i-1]+m)%i。當然,i=1就是特殊情況了,f[1]=0。這裏還有一個小問題。也許你會迷惑爲什麼x`=(x+m%n)%n=(x+m)%n中的%n變成公式中f[i]=(f[i-1]+m)%i中的%i?其實這個稍微想想就能明瞭。我們%n就是爲了從序列c轉換到序列b——這是在n-1序列轉換成n序列時%n;那麼從n-2轉換到n-1呢?不是要%(n-1)了嗎?所以這個值是變量,不是常量。

好了,這個最後需要注意的就是從一開始,我們將n序列從0~n-1編號,所以依據公式1得出的序號是基於0開始的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章