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

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

uva live 3882 And Then There Was One 约瑟夫环的相关文章

UVA 10935 约瑟夫环

Throwing cards away I Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Description Given is an ordered deck of n cards numbered 1 to n with card 1 at the top and card n at the bottom. The following operation is per

UVa 133 双向约瑟夫环

背景:1_TlE:没有考虑到,当k,m很大的时候,就会用太多时间,那么我想到了: k=k%n+n;// 之所以要加n,是为了避免,k是n的倍数时,k等于0. m=m%n+n; 2_WA:经过_TLE:之后没有完善,当k不是n的倍数时就不能加n!终究来说还是没有测试所有数据,以后切题,就把所有数据保存在记事本,要全部通过,才提交!! 好多人都说这是一个双向链表的数据结构题,被我数组模拟过了,双向约瑟夫环... 思路:小紫书在这里出这道题,是想让我们锻炼自顶向下的程序框架思想,即:想建立大框架,一些

&#183;读书笔记」 具体数学

头脑一热,然后买了<具体数学>,差点还买了英文版后来觉得自己英文太烂,还是决定购买的中文版书是前几天到的,从2014.8.1号开始看,坚持每天看一点,学习一点.为了监督自己,决定在这里开一贴,记录我的学习路程. 时间:2014.8.1  地点:家里 今天阅读的是本书的第一章,讲的是递归问题,也是算法的基础,当然这里主要讲的是数学.但是没办法,我是一名ACMer,必须要和程序紧密联系在一起.我觉得明天还需要花一天时间来看这章,需要解决下后面的习题以及再加深点体会. 第一章 递归问题 1.1 汉诺

UVA 3882【dp】【简单数学】

题意: 给定三个数分别是: 人数    间隔     起点 题目中人的编号从1开始.在进行约瑟夫环的判定之后,求解最后能够活下来的人. 思路: 约瑟夫环的递推公式是 f[n]=(f[n-1]+jiangeshu)%zongrenshu. f[1]=0. 注意公式中的人是从0开始编号的. 由于给定了起点所以需要修正偏移.这里我的理解是把最后一次的间隔人数看作是起点而不是给定的间隔人数,因为通过递推公式得到的是从第0个开始算起的,那么最后修改了间隔人数正好保证第一个出来的人是第起点个.而以后出来的人

UVA 562 Dividing coins --01背包的变形

01背包的变形. 先算出硬币面值的总和,然后此题变成求背包容量为V=sum/2时,能装的最多的硬币,然后将剩余的面值和它相减取一个绝对值就是最小的差值. 代码: #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define N 50007 int c[102],d

UVA 10341 Solve It

Problem F Solve It Input: standard input Output: standard output Time Limit: 1 second Memory Limit: 32 MB Solve the equation: p*e-x + q*sin(x) + r*cos(x) + s*tan(x) + t*x2 + u = 0 where 0 <= x <= 1. Input Input consists of multiple test cases and te

UVA 11014 - Make a Crystal(容斥原理)

UVA 11014 - Make a Crystal 题目链接 题意:给定一个NxNxN的正方体,求出最多能选几个整数点.使得随意两点PQ不会使PQO共线. 思路:利用容斥原理,设f(k)为点(x, y, z)三点都为k的倍数的点的个数(要扣掉一个原点O).那么全部点就是f(1),之后要去除掉共线的,就是扣掉f(2), f(3), f(5)..f(n).n为素数.由于这些素数中包括了合数的情况,而且这些点必定与f(1)除去这些点以外的点共线,所以扣掉.可是扣掉后会扣掉一些反复的.比方f(6)在f

[UVa] Palindromes(401)

UVA - 401 Palindromes Time Limit: 3000MS   Memory Limit: Unknown   64bit IO Format: %lld & %llu Submit Status Description A regular palindrome is a string of numbers or letters that is the same forward as backward. For example, the string "ABCDED

uva 401.Palindromes

题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=342 题目意思:给出一段字符串(大写字母+数字组成).判断是否为回文串 or 镜像串 or 回文镜像串 or 什么都不是.每个字母的镜像表格如下 Character Reverse Character Reverse Character Reverse A A M M Y Y B