Leo_h

2333

约瑟夫问题汇总

背景

有编号从1NN个小朋友在玩一种出圈的游戏。开始时N个小朋友围成一圈,编号为I+1的小朋友站在编号为I小朋友左边。编号为1的小朋友站在编号为N的小朋友左边。首先编号为1的小朋友开始报数,接着站在左边的小朋友顺序报数,直到数到某个数字K时就出圈。直到只剩下1个小朋友,则游戏完毕。

1、求最后出圈的人

为了方便进行取模,假设还剩n个人的时候编号为0~n-1
令一个人出圈以后从他的下一个开始编号为0,一直到他的前一个编号为n-2,那么不难发现,每个人的旧编号都是新编号加上k mod n的结果,这是因为被删去的数在新的编号法则中编号为n-1。这相当于将k~(n-1)~0~k-1重新编号成0~n-1,再删去n-1,对所有剩余的编号都没有影响
这样,最后出圈的人在仅剩一人时编号为0,每次+k mod 人数 可以递推得剩更多人时胜利者的编号,直至n人
时间效率:O(n)
题目链接:La3882
#include<cstdio>
using namespace std;
int n,k;
int f;
int main()
{
 	scanf("%d%d",&n,&k);
 	while(n&&k)
 	{
 		f=0;
 		for(int i=2;i<=n;i++)
 			f=(f+k)%i;
		printf("%d\n",f+1);
		scanf("%d%d",&n,&k);
	 }
}

2、求倒数第M个出圈的人

与上面的思路类似。
还剩M个人的时候,从0开始数,下一个出圈的编号为(k-1)%M,然后向上递推即可
时间效率 O(n)
题目链接:LA4727
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int T,n,k,f;
int main()
{
	scanf("%d",&T);
	while(T--)
	{	
		scanf("%d%d",&n,&k);
		f=(k-1)%3;
		for(int i=4;i<=n;i++)
			f=(f+k)%i;
		printf("%d ",f+1);
		f=(k-1)%2;
		for(int i=3;i<=n;i++)
			f=(f+k)%i;
		printf("%d ",f+1);
		f=0;
		for(int i=2;i<=n;i++)
			f=(f+k)%i;
		printf("%d\n",f+1);
	}
}

3、输出完整的出圈顺序

1、模拟法

使用一个环状链表,真正进行报数操作,由于并不困难,在这里不给出代码
时间效率:O(n^2)

2、线段树

建一棵权值线段树,每个节点存储大小在【l,r】之间的数的个数,则可以在线段树上二分,找出剩余n个数字中的第k%n个然后删去
时间效率:O(nlogn)
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define lch t*2
#define rch t*2+1 
#define mid (l+r)/2
#define maxn 30005
int n,m;
int size[maxn*4]; 
void del(int x,int t=1,int l=1,int r=n)
{
	size[t]--;
	if(l==r) return;
	x<=mid?del(x,lch,l,mid):del(x,rch,mid+1,r);
}
int kth(int k,int t=1,int l=1,int r=n)
{
	if(l==r) return l;
	if(k<=size[lch]) 
		return kth(k,lch,l,mid);
	else return kth(k-size[lch],rch,mid+1,r);
}
void build(int t=1,int l=1,int r=n)
{
	size[t]=r-l+1;
	if(l==r)return;
	build(lch,l,mid);
	build(rch,mid+1,r);
}
int main()
{
 	scanf("%d%d",&n,&m);
 	build();
 	int lastdel=0;
 	while(size[1])
 	{
 		lastdel=(lastdel+m)%size[1]; 
		if(lastdel==0) lastdel=size[1];
		int ans;
		del(ans=kth(lastdel)); 
		lastdel--;
		printf("%d ",ans);
	}
}




阅读更多
版权声明:本文为博主原创文章,转载请注明源网址blog.csdn.net/leo_h1104 https://blog.csdn.net/Leo_h1104/article/details/52915583
文章标签: DP
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

约瑟夫问题汇总

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭