码迷,mamicode.com
首页 > 其他好文 > 详细

uva live 3882 And Then There Was One 约瑟夫环

时间:2015-06-01 22:39:16      阅读:171      评论:0      收藏:0      [点我收藏+]

标签:uva live 3882   and then there was o   约瑟夫环   

// 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;
}

uva live 3882 And Then There Was One 约瑟夫环

标签:uva live 3882   and then there was o   约瑟夫环   

原文地址:http://blog.csdn.net/timelimite/article/details/46316025

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!