您的位置:首页 > 其它

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

2016-04-11 16:33 169 查看
模拟:

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--;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: