BestCoder Round #75 King's Game

本文介绍了一个类似于约瑟夫环的问题及其解决方案。通过递推的方式解决了问题,提供了预处理方法,使得查询时间复杂度降低至O(1)。

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

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5643

这道题是一个类似于约瑟夫环的问题,但是跳动数每次都+1,小编先给出这道题的官方题解:

King's Game

约瑟夫问题的一个变种,然而题目全部是在唬人,就是一个简单的递推。虽然我知道有人会打表。。。

我们看看裸的约瑟夫是怎么玩的:nn 个人,每隔 kk 个删除。

由于我们只关心最后一个被删除的人,并不关心最后的过程,所以,我们没有必要也不能够模拟整个过程。我们用递推解决。假设有nn个人围成环,标号为[0,n-1][0,n1]00开始的好处是取模方便),每数kk个人杀一个的情况下,最后一个存活的人的编号是f[n]f[n]

我们有f[1]=0f[1]=0,这不需要解释。

接着考虑一般情况f[n]f[n],第一个杀死的人的编号是k-1k1,杀死后只剩下n-1n1个人了,那么我们重新编号!

原来编号为k的现在是00号,也就是编号之间相差33我们只要知道现在n-1n1个人的情况最后是谁幸存也就知道nn个人的情况是谁幸存。幸运的是f[n-1]f[n1]已经算出来了那f[n]f[n]就是在f[n-1]f[n1]的基础上加上一个kk即可不要忘记总是要取模。

https://i-blog.csdnimg.cn/blog_migrate/dd08820945ffd7b3ef2924268079faad.png

所以递推式子是: f[i] = \begin{cases} & \text{ 0 \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ } i=1 \ & \text{ (f[i - 1] + k) mod i \ \ \ \ \ \ } other \end{cases}f[i]={ 0                      i=1 (f[i - 1] + k) mod i       other

此题只用在原版约瑟夫问题上加一维,由于依次隔 1, 2, 3...n - 11,2,3...n1 个人删除,所以用 f[i][j]f[i][j] 表示 ii 个人,依次隔 j, j + 1... j + i - 1j,j+1...j+i1 个人的幸存者标号。

根据刚才的重标号法,第一次 j - 1j1 号出局,从 jj 开始新的一轮,从 j + 1j+1 开始清除,剩余 i - 1i1 个人,也有递推式子:

f[i][j] = \begin{cases} & \text{ 0 \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ } i=1 \ & \text{ (f[i - 1][j+1] + j) mod i \ \ \ \ \ \ } other \end{cases}f[i][j]={ 0                      i=1 (f[i - 1][j+1] + j) mod i       other

答案就是 f[n][1] + 1f[n][1]+1(将标号转移到 [1, n][1,n]),问题轻松解决。

复杂度:预处理 O(n^2)O(n2),查询 O(1)O(1),总复杂度 O(n^2)O(n2)。由于可以滚动数组以及常数太小,所以 nn 给了 50005000(其实是出题人不会别的算法嘿嘿嘿)。


比赛的时候由于太多时间浪费在了手机那倒水题上(小编TT),这道题并没有怎么看,当时看着觉得是模拟可能会超时,但是又想不出什么方法就没怎么看,下来看到题解的时候也是惊呆了,果然DP是个玄学啊,这东西还能这样递推……(好吧,是小编太辣鸡了),由于题解写的很清楚了,小编这里就只做一点小小的讲解就好,因为n个人最后留下来的那个人是确定的不会改变的,所以我们并不需要每次都去算一次,预处理打表就可以解决。

#include <cstdio>
#include <cstring>
const int maxn = 5000+5;
int ans[maxn];
int main()
{
	ans[1] = 1;
	for(int i=2; i<=5000; i++)
	{
		int tmp = 0;
		for(int j=2; j<=i; j++)
		{
			tmp = (tmp+i-j+1) % j;
		}
		ans[i] = tmp+1;
	}
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n;
		scanf("%d",&n);
		printf("%d\n",ans[n]);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值