结合UVALive 3882, And Then There Was One ,谈谈个人对约瑟夫环问题的理解

题目:https://vjudge.net/problem/UVALive-3882

题意:n个人围成一个环(1~n),一开始先干掉第m个人,从第m个人开始每数k个干掉一个,问最后剩下的那个人编号是多少

注意题目n<=10000,暴力会超时

这道题是约瑟夫环的变形,为了解决这道题,需要我们先解决约瑟夫环问题

约瑟夫环问题:

问题:n个数围成一环(1~n),第一个人开始报数,每k个数干掉一个,问最后剩下的那个人编号是多少。

没错和变式题相比少了一个:一开始先干掉第m个人。

那么约瑟夫环问题怎么解呢?这个网上也有很多资料,我就写一下我自己的理解。

首先,我们使用编号为 0~n-1 来为n个人编序号(至于原因末尾会讲)

假如我们用f(n) 表示有n个人的约瑟夫环问题的答案,那么我们可以使用类似于动态规划的思路,寻找子问题,来解决。

显然,对于子问题,我们已经找到了递推的起点,就是f(1)=0,只有一个人,那么那个人就是答案。

找到了递推的起点,那么问题来了,怎么通过f(n-1) 推出 f(n)呢

既然f(n-1)表示的是f(n)的子问题,也就是n-1个人的约瑟夫环问题,那么我们在f(n)中找到被干掉的那个人,以那个人的下一个人为起点重新编号,就可以了

当然这还不够,这样的f(n)和f(n-1)还是独立的,我们还需要找出f(n-1)到f(n)的递推式

最后输出答案的时候就将编号+1即可,这就是约瑟夫环问题的思路。

回到这题:

这道题需要我们一开始就干掉一个人

那我可以将约瑟夫问题直接套在这道题身上,然后输出(ans+m)%n吗? (ans是约瑟夫环f(n)的值)

你看,你让我一开始就干掉第m个,假如f(n)为约瑟夫环问题的答案,那么我将每个人都往后推m个单位进行计算不就行了吗。(这也是我一开始的想法)

emmmm,这样是不行的。

一开始就干掉第m个,相当于人数变成了n-1个,就相当于f(n-1)了呀,还怎么使用f(n)的方法做

那么应该怎么做呢?

注意到,当干掉第一个人之后,第二个人开始就成了约瑟夫环问题了,因此本题应该分两个部分

f(n)到 f(n-1)需要我们找新的递推式,而f(n-1)到f(1)直接套用约瑟夫环解决即可

方法怎么样呢,其实和上面方法一样,读者可以自己试一下

代码:

#include<iostream>
#include<cstdio>
using namespace std;
//int dp[maxn]; 这道题可以不使用数组 
int n,k,m;
int main(){
	while(scanf("%d %d %d",&n,&k,&m),m){
		int res=0;
		for(int i=2;i<n;i++){ //n-1阶 约瑟夫环问题 
			res=(res+k)%i; // %i 每个环长度都不一样 
		}
		res=(res+m)%n; //自己找规律得到的式子,m也要-1记得呀 
		cout<<res<<endl;
	}
	return 0;
}

最后:为啥要使用0~n-1

你试试1~n就知道了(滑稽)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值