题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1883

题意:n个人围成一圈,第一次删第m个人,然后每数K个删一个人,求最后一个人的编号

分析:典型的约瑟夫问题,和杀人游戏差不多,杀人游戏描述如下:

推导过程:

首先,我们要对问题描述改一下,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)这样,光秃秃的约瑟夫环问题就结束了。。。

 #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=;
ll dp[N]={};
int main()
{
ll n,k,m;
while(scanf("%lld%lld%lld",&n,&k,&m)!=EOF)
{
if(n==||k==||m==)
break;
for(int i=;i<n;i++)
dp[i]=(dp[i-]+k)%i;
dp[n]=(dp[n-]+m)%n;
printf("%lld\n",dp[n]+);
}
}

第一次:        0.1.2 ..... k-1.k.k+1........n-1

去掉k - 1     0.1.2 .....       k.k+1........n-1

从k开始       k.k+1...... n-1.0.1........ k-2

转换           0.1..........n-2  (转换公式: ( i - k + n ) % n )

第二次        0.1..........n-2

去掉k - 1     0.1.2 .....       k.k+1........n-2

从k开始       k.k+1...... n-2.0.1........ k-2

转换           0.1..........n-3  (转换公式: ( i - k + n - 1 ) % ( n - 1 ) )

........

第n次         0

反向递推

a = 0

第n次的0等价于第n-1次。最后一个删除的数  a = ( a + k  ) % 2

a 等价于第n - 2次。最后一个删除的数         a = ( a + k ) % 3

......

a = ( a + k ) % n

那么递推公式出来了

a = 0;
for( int i = 2; i <= n; i ++ ) a = ( a + k ) % i;

再根据m - 1 与 k - 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编号就可以了。。。

下面给出AC代码:

UVALive 3882 - And Then There Was One【约瑟夫问题】的更多相关文章

  1. UVA 1394 And Then There Was One / Gym 101415A And Then There Was One / UVAlive 3882 And Then There Was One / POJ 3517 And Then There Was One / Aizu 1275 And Then There Was One (动态规划,思维题)

    UVA 1394 And Then There Was One / Gym 101415A And Then There Was One / UVAlive 3882 And Then There W ...

  2. LA 3882 And Then There Was One[约瑟夫问题的变形]

    And Then There Was One UVALive - 3882 Sample Input   Sample Output //设f[i]为(原约瑟夫问题)第i次要删除的标号 #includ ...

  3. LA 3882 - And Then There Was One(约瑟夫 递归)

    看题传送门 题目大意: N个数排成一圈,第一次删除m,以后每k个数删除一次,求最后一被删除的数. 如果这题用链表或者数组模拟整个过程的话,时间复杂度都将高达O(nk),而n<=10000,k&l ...

  4. UVALive - 3882:And Then There Was One

    约瑟夫环 f[i]表示有i个人先处理第k个人,最后被处理的人是谁 #include<cstdio> #include<cstdlib> #include<algorith ...

  5. UVALive 3882.And Then There Was One-约瑟夫问题(递推)

    And Then There Was One Time limit: 3.000 seconds Let’s play a stone removing game. Initially, n ston ...

  6. 大白第一章第四节dp例题

    入口 UVALive - 3882 #include<cstdio> using namespace std; ; int n,m,k,f[N]; int main(){ //f[i]表示 ...

  7. LA 3882 经典约瑟夫环问题的数学递推解法

    就是经典约瑟夫环问题的裸题 我一开始一直没理解这个递推是怎么来的,后来终于理解了 假设问题是从n个人编号分别为0...n-1,取第k个, 则第k个人编号为k-1的淘汰,剩下的编号为  0,1,2,3. ...

  8. 约瑟夫问题(java实现)

    方法一.自定义的链表实现 package com.code.yuesefu; public class YueSeFuList { public static void main(String[] a ...

  9. Java 解决约瑟夫问题

    约瑟夫问题(有时也称为约瑟夫斯置换,是一个出现在计算机科学和数学中的问题.在计算机编程的算法中,类似问题又称为约瑟夫环.又称“丢手绢问题”.) 有这样一个故事,15个教徒和15个非教徒在深海遇险必须讲 ...

随机推荐

  1. ES6数组及数组方法

    ES6数组可以支持下面的几种写法: (1)var [a,b,c] = [1,2,3]; (2)var [a,[[b],c]] = [1,[[2],3]]; (3)let [x,,y] = [1,2,3 ...

  2. iOS 常用到的宏#define

    //AppDelegate #define APPDELEGATE [(AppDelegate*)[UIApplication sharedApplication] delegate] //----- ...

  3. 转:IT巨头纷纷“卡位” 智能语音成人机交互入口必争之地

    http://www.cs.com.cn/xwzx/hwxx/201707/t20170712_5368595.html 随着物联网的迅速发展,作为重要接口的智能语音技术已成为国内外IT巨头的必争之地 ...

  4. laravel框架一种方便的快速填充数据的方法

    首先大家都知道在laravel框架里是采用seeder来填充数据的,具体命令如下,请将如下的类名称替换成你具体的seeder类名. 首先创建seeder类 php artisan make:seede ...

  5. 为什么硬链接不能链接目录、文件inode 和目录 dentry 的区别联系

    我们对任何一个目录用ls -l 命令都可以看到其连接数至少是2,这也说明了系统中是存在硬连接的,而且命令ln -d 也可以让超级用户对目录作硬连接,这些都说明了系统限制对目录进行硬连接只是一个硬性规定 ...

  6. linux svn up 中文显示乱码解决办法

    vi /etc/sysconfig/i18n #LANG="en_US.UTF-8" #LANG=zh_CN.GB18030 #LC_ALL=zh_CN.GB18030 #SYSF ...

  7. python实现散列表的直接寻址法

    散列表(Hash table,也叫哈希表),是根据键(Key)而直接访问在内存存储位置的数据结构.也就是说,它通过计算一个关于键值的函数, 将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速 ...

  8. TurnipBit口袋编程计算机:和孩子一起DIY许愿的流星

    听说对着流星许愿,许的愿望都会实现,虽然不知道这个说法是不是真的,但是流星还是很好看的,为了能一直看到流星,今天就自己做一个流星保存下来,想什么时候看,就什么时候看. 首先需要想象一下流星是什么样子的 ...

  9. C#高级编程学习一-----------------第五章泛型

    三层架构之泛型应用 概述 1.命名约定 泛型类型以T开头或就是T. 2.泛型类 2.1.创建泛型类

  10. 什么是TNB?如何买TNB?

    我天天在微博上看到有人吹TNB,今天一起来看下它到底是什么玩意? 什么是TNB? Time New Bank (TNB) 旨在打造精淮的时间价值传输网络.   我们高度认可人们时间付出的商业价值,每个 ...