// uva live 3882 And Then There Was One // // 经典约瑟夫环问题。n是规模,k是每次数的人数,m是第一个出列的人。 // // 但是暴力用链表做肯定是不行的,因为 1 <= n <= 10000 , 1<= k <= 10000 // 1 <= m <= n; 虽然我知道公式是什么,但是我并不会推导,看了几乎一个下午的 // 数学推导过程,又弄了几个样例亲自动手实验一下,这样才算是有了一点明悟。 // 下面来分享一下自己能力范围的理解过程。 // 推导过程。 // // 首先,我们要对问题描述改一下,n个人编号为0,1,2,....,n-1,f[n]表示n个人组成 // 的约瑟夫环按照规则后最后一个存活的编号。 // // 这样之后,首先,我们知道,第一个出列的人的编号(m-1)%n,则我们可以从 // 这个人的后面编号设为k= m % n,则这n-1个人的编号依次为k,k+1,....n- // 1,n,1,2,...k-2; // 则重新编号为0,1,2....n-2,那么我们就可以看作是在这n-1规模的子问题的约 // 瑟夫环的基础上,求解n规模的约瑟夫环。 // // 设n-1规模的子问题的约瑟夫环的解为f[n-1],则n规模约瑟夫环是 // f[n] = (f[n-1] + k) % n; // 证明过程如下: // 原来编号依次为k,k+1,....n-1,n,1,2,...k-2; // 重新编号以后依次为0,1,2,....,n-2; // // f[n] = (f[n-1] + k) % n;(加上k是因为编号要变成n规模里面的编号,不太明白的 // 请看上面的序列) // 而 k = m % n; // 则f[n] = (f[n-1] + m) % n; // // 递推公式是f[i] = (f[i-1] + k) % i; // 而f[1] = 0,则最后的结果是f[n]+1(因为f[n]是0,1...n-1编号的,所以要加1) // // 这样,光秃秃的约瑟夫环问题就结束了。。。 // // 在这题中,m是开始第一个跳出的人,我们并不需要在一开始的时候从m开始, // 我们依然从 0 开始,只是最后的结果偏移一下就可以,具体偏移多少,慢慢道来 // // 首先,我要强调一点,推导都是从0开始的,这一点请大家牢记心中,而约瑟夫环 // 的问题特征则是,从不同的位置算起,出列的依次顺序比从0开始的顺序依次顺序 // 整体向右平移了几个单位。 // n=8,k=5,m=3为例,出列依次为3,8,6,5,7,2,4,1 // n=8,k=5,m=1为例,出列依次为5,2,8,7,1,4,6,3 // 整体向右偏移了2,(对n取膜的情况下) // // 那么,我们只要确定起点就可以了,因为编号是0,1,2,...n-1, // 而我们是从m(m是1到n)开始算,则起点的偏移就是m-1,而第一个m-1是出队的 // 我们算的是0开始数k个才是第一个出队的,所以,必须再减掉k-1个偏移(相当于 // 算m-1是第一个出列的,算起点就是减掉k-1个,起点也算一个哟),这样最后的起点 // 的偏移就可以算出来了0 + m - 1 - ( k - 1 ) = m - k; // 因为偏移量是相同的,那么终点也是这么多则结果为f[n] + m - k 是最终的结果 // 而这个最终的编号是0,1,2...n-1(n-1编号之内)。最后的结果在加上一个1, // 最后结果f[n] + m - k + 1,最后对n取膜,保障在1到n编号就可以了。。。 // // 至此,这道题的解析就正式结束了。。。。 // // 解题感悟 // // 以前做过朴素的约瑟夫环,知道这个公式,但是这道题却是完全不会,只要变一点 // 就不会做了,最后狠下心来,几乎这一天都在啃这个公式。最后终于有了一丝理解 // // // 这道题目的体悟在于,在推公式的时候,当自己认为是理所当然的时候,请停下来 // 问问自己,为什么,在推导这个公式的时候,虽然对于一些人来说很简单,但是对 // 于我来说,是挺难的,对于我这蒟蒻来说。所以,在我以为差不多的时候,我停下 // 来了,问了自己为什么,发现自己,完全不能回答自己的问题,那么,我就再重新 // 的推导,百思不得其解,为什么会是这样。苦思冥想,最后,猛然间,一丝的明悟 // 涌上心头,一阵狂喜。哈哈哈哈,这种感觉很奇妙,很充实。 // // 最后,还是,继续练吧,路还长着呢,继续走吧 #include <algorithm> #include <bitset> #include <cassert> #include <cctype> #include <cfloat> #include <climits> #include <cmath> #include <complex> #include <cstdio> #include <cstdlib> #include <cstring> #include <ctime> #include <deque> #include <functional> #include <iostream> #include <list> #include <map> #include <numeric> #include <queue> #include <set> #include <stack> #include <vector> #define ceil(a,b) (((a)+(b)-1)/(b)) #define endl '\n' #define gcd __gcd #define highBit(x) (1ULL<<(63-__builtin_clzll(x))) #define popCount __builtin_popcountll typedef long long ll; using namespace std; const int MOD = 1000000007; const long double PI = acos(-1.L); template<class T> inline T lcm(const T& a, const T& b) { return a/gcd(a, b)*b; } template<class T> inline T lowBit(const T& x) { return x&-x; } template<class T> inline T maximize(T& a, const T& b) { return a=a<b?b:a; } template<class T> inline T minimize(T& a, const T& b) { return a=a<b?a:b; } const int maxn = 10008; int n,m,k; int f[maxn]; void fun(){ f[1] = 0; for (int i=2;i<=n;i++) f[i] = (f[i-1] + k) % i; } int main() { //freopen("G:\\Code\\1.txt","r",stdin); while(scanf("%d%d%d",&n,&k,&m)!=EOF){ if (!n && !m && !k) break; fun(); int ans = (f[n] + m - 1 + 1 - k + 1)%n; if (ans<=0) ans += n; printf("%d\n",ans); } return 0; }
时间: 2024-10-02 15:00:26