【codevs 1282】 约瑟夫问题 【题解】

模拟:

STL队列:

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#define inf 0x3f3f3f3f
#define LL unsigned long long
#include<queue>
using namespace std;
int n,m;
queue<int> q;
/******Program Begin*********/
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)q.push(i);
	int cnt=0;
	while(!q.empty())
	{
		int t = q.front();
		q.pop();
		cnt++;
		if(cnt%m==0)
		{
			cnt=0;
			printf("%d ",t);
			continue;
		}
		else q.push(t);
	}
	return 0;
}
/*******Program End**********/


#include"stdio.h"

int main()

{

int m,n,i,j;

int s=1;

int a[30005];

scanf("%d%d",&n,&m);

for(i=0;i<=n;i++)

<span style="white-space:pre">	</span>a[i]=i;

while(n){

i=(s+m-1)%n;//出列的下标

if(i==0) i=n;

printf("%d ",a[i]);

for(j=i;j<n;j++)//删除出列的

a[j]=a[j+1];

n--;

s=i;//下一个开始数的位置

}

return 0;

}


线段树:

先把所有1~n的编号全都插入一棵二叉树(别顺序插入,可以随机序也可以分治递归),表示当前状态存活的人的集合,并维护每棵子树的节点数size

关键是要删除一个点后如何快速找到下次该删哪个点

我们可以维护当前存活的人数,删除的时候也需要搜索一并求出这个点在二叉树中的rank值,很容易就可以找出下一个要删掉点的rank值

根据这个rank值删除下一个点,这样做n遍

由于只有删除操作普通二叉树就够了(不会退化),不过要是闲的话写个平衡的也没问题

个人认为块链也可以,并且更易实现

#include<cstdio>

#include<iostream>

using namespace std;

struct tre{

int a,b,s;//a表示左边界, b表示右边界 ,s表示当前节点下方有多少个叶子节点。 

}tree[100000];//必须开1000000,线段树一般需要n*3的空间 



int n,m;



void change(int	x){

tree[x].s=tree[x*2].s+tree[x*2+1].s; 

}



void buildtree(int a,int b,int x){//建设线段树,x为tree中编号 

tree[x].a=a;

tree[x].b=b;//左右边界赋值 

if (a==b) {

tree[x].s=1;

return;

}//叶子节点返回值 

int mid=(a+b)/2;

buildtree(a,mid,x*2);

buildtree(mid+1,b,x*2+1);//拆分成两段 

change(x);//算出当前节点的s权值 

}



int addtree(int t,int x){//删叶子,t指tree中序号,x指要删除的叶子,前面应该有几个叶子。 

if (tree[t].a==tree[t].b){

tree[t].s=0;

return tree[t].a;

}//找到了要被剔除的叶子节点 

int temp;

if (tree[2*t].s>=x) temp=addtree(2*t,x);//如果不是叶子,往两侧找 

else temp=addtree(2*t+1,x-tree[2*t].s);

change(t);

return temp;//返回那个被删除的叶子 

}



int main(){

cin>>n>>m;

int cur=1,x=0,left=n;

buildtree(1,n,1);

for (int i=1;i<=n;i++){

x=(cur+m-1) % left;//计算从1开始数之前有多少个叶子, 

if (0==x) x=left;//特别注意,模出0就意味着数到头 

cur=x;

left--;

cout<<addtree(1,cur)<<" ";

}

return 0;

}




另pascal党的福利:
var
  n,m,i,place:longint;
  a:array[1..30000]of longint;
begin
  read(n,m);
  for i:=1 to n do a[i]:=i;
  place:=0;
  for i:=n downto 2 do begin
    inc(place,m);
    while place>i do dec(place,i);
    write(a[place],' ');
    move(a[place+1],a[place],(i-place)<<2);//Pascal传说中的block操作
    dec(place);
  end;
  writeln(a[1]);
end.

线段树,模拟每次离队的是场上第几个

#include<stdio.h>
int n,m,k,v;
struct Trees{
    int l,r,sum;
}t[120001];
void Get_tree(int L,int R,int k)
{
    t[k].l=L; t[k].r=R; t[k].sum=R-L+1;
    if(L==R) return;
    Get_tree(L,(L+R)>>1,k<<1);
    Get_tree(((L+R)>>1)+1,R,(k<<1)+1);
}
void Move(int k,int w)
{
    t[k].sum--;
    if(t[k].l==t[k].r){ printf("%d ",t[k].l); return; }
    if(t[k<<1].sum>=w) Move(k<<1,w);
    else Move((k<<1)+1,w-t[k<<1].sum);
}
main()
{
    scanf("%d %d",&n,&m);
    Get_tree(1,n,1);
    k=n;
    while(k)
    {
        v=(v-1+m)%k;
        Move(1,v+1);
        k--;
    }
}



你可能感兴趣的:(【codevs 1282】 约瑟夫问题 【题解】)