約瑟夫環是一個非常經典的數學問題,n個人圍成一圈,從1開始報數,每當有人報到m時,他被淘汰,下一個人繼續從1開始報數,問最後的獲勝者是誰。 有一種很經典的演算法是用鏈表/數組來類比整個遊戲的過程,但是當n , m 都很大的時候這個方法就顯然行不通了。有沒有更便捷的方式來解決這個問題呢。 我們可以從數學的角度來分析一下這個問題。首先我們通過思考可以得到一個很顯然的結論,n個人時的獲勝者和這n個人淘汰掉一個人繼續遊戲下的獲勝者一定是同一個人。(有點像廢話...)所以如果我們能確定 n-1個人時候的獲勝者也就可以得到 n 個人時候的情況,因為兩者的排列存在一種對應關係。 考慮n = 6 m = 3的情況 0 1 2 3 4 5 3 4 5 0 1 2 可以很明顯的看出上下兩行存在著 + m 模 n的對應關係。 這樣這個問題就可以通過遞推的方式解決了, f[i] 為 場上有 i 個人時,最後獲勝者的編號(從零開始)。 那麼就存在著這樣的遞推式 f[i] = ( f[i-1] + k ) % i 我們還可以知道 f[1] = 0 所以可以通過這個遞推式輕鬆的得到答案啦~
代碼實現:(HDU 2925)
/* @Author: wchhlbt @Date: 2017/3/9*/#include <bits/stdc++.h>#define Fori(x) for(int i=0;i<x;i++)#define Forj(x) for(int j=0;j<x;j++)#define maxn 1005#define inf 0x3f3f3f3f#define ONES(x) __builtin_popcount(x)using namespace std;typedef long long ll;typedef long double ld;typedef pair<int,int> P;const double eps =1e-8;const int mod = 10007;const double PI = acos(-1.0);int dx[4] = {0,0,1,-1};int dy[4] = {1,-1,0,0};int main(){ int n,d; while(cin>>n>>d && n+d){ int ans = 0; for(int i = 2; i<=n; i++) ans = (ans+d)%i; cout << n << " " << d << " " << ans+1 << endl; } return 0;} 這個問題還可以進一步最佳化,當n非常大,k卻很小的時候,我們甚至沒有必要遍曆所有的 i ,考慮這個式子ans = (ans+d)%i , 當 i已經很大了的時候可能 ans + k*d < i , 這種情況下我們完全沒有必要一步一步的去類比,因為這個數值始終沒有變化,所以可以一次性跳躍若干的值達到最佳化演算法的目的。
AC代碼:(HDU 3089)
/* @Author: wchhlbt @Date: 2017/3/14*/#include <bits/stdc++.h>#define Fori(x) for(int i=0;i<x;i++)#define Forj(x) for(int j=0;j<x;j++)#define maxn 1005#define inf 0x3f3f3f3f#define ONES(x) __builtin_popcount(x)using namespace std;typedef long long ll;typedef long double ld;typedef pair<int,int> P;const double eps =1e-8;const int mod = 10007;const double PI = acos(-1.0);int dx[4] = {0,0,1,-1};int dy[4] = {1,-1,0,0};int main(){ ll n,k; while(scanf("%I64d%I64d",&n,&k)!=EOF){ if(k==1){ printf("%I64d\n",n); continue; } ll ans = 0; for(ll i = 2; i<=n; ){ if(ans+k<i){ ll num; if((i-1-ans)%(k-1)==0) num = (i-1-ans)/(k-1) - 1; else num = (i-1-ans)/(k-1); if(i+num>n){ ans = (ans + (n+1-i)*k)%n;//因為目前ans儲存的是只有i-1個人時的倖存編號 break; } i += num; ans = (ans + num*k)%i; } else{ ans = (ans+k)%i; i++; } } printf("%I64d\n",ans+1); } return 0;}