UVALive - 3882 And Then There Was One (递推[dp])

本文介绍了一种解决约瑟夫问题的高效算法。通过调整编号方式和使用递推公式,实现对大规模数据集的有效处理,避免了传统模拟方法带来的超时问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大体题意:

给你一个由n 个数(1~n)组成的圆环,刚开始删除m,以后每数k 个数删除一个数,求最后剩下哪个数?

思路:

这个题数据量比较大,什么数组模拟,链表模拟, 都会超时!

这个题目不就是猴子选大王吗= =!

看了看相关约瑟夫问题的讲解,感觉很巧妙!简单记录一下:

有这么一类问题:

给你1~n 的圆环,你从1报数,报到m 停止删除这个数,继续报,求最后的人?

因为涉及到取余问题,可以改变一下数据,给你一个0~n-1 的圆环,你从0报数,报到m-1停止,删除这个数,继续报,求最后的人?

这两个问题是一样的!

第一次报数肯定是m%n 这个位置就退出了! 我们令 k = m % n

那么剩下的人就是:

k   k+1   k+2 .... n-1  0  1  2  ... k-2

我们可以给他们重新编号:

k--->0

k+1 ---> 1

k+2 ---->2

k-2 ----->n-2

这样只剩下了n-1个人,假设我们知道这n-1个人的结果  为dp[n-1]的话,那么很容易推出  n 个人是多少了!

观察数据也可以知道  dp[n] = (dp[n-1] + k) % n

而dp[1] = 0  (显然),因此这个递推式就有了!

这样dp[n]表示的是   从0~n-1 这个n个人报数  报到k删除 继续的结果!!

然而这个题目 是 一开始就删除了m  ,  这就相当于  从m-k+1开始报数

也就是由原来的0 ---> m-k+1

因此我们要给dp[n] += m-k+1适合这个题目!

注意取模问题!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 10000 + 10;
int dp[maxn];
int main(){
	int n,k,m;
	while(scanf("%d %d %d",&n, &k, &m) == 3 && (n || k || m)){
		dp[1] = 0;
		for (int i = 2; i <= n; ++i){
			dp[i] = (dp[i-1] % i + k % i) % i;
		}
		dp[n] = (dp[n]%n + (m-k+1) % n) % n;
		if (dp[n] <= 0)dp[n] += n;
		printf("%d\n",dp[n]);
	}
	return 0;
}
/*
8 5 3
100 9999 98
10000 10000 10000
0 0 0
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值