约瑟夫问题链表模拟解法c语言,【数据结构】P1996 约瑟夫问题

100 ≤ m,n ≤ 100

【题解】

问题其实并不困难,但是目的就是利用题目来锻炼自己的数据结构。

给出一题五解的做法。

【解法一】

利用STL里面的list,注意指针到了链表尾部要指回链表的头部。

#include

using namespace std;

typedef long long ll;

int n,m;

int main()

{

list List ;

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

for(int i=;i<=n;i++) List.push_back(i);

list ::iterator it = List.begin(),tmp;

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

for(int j=;j

it++;

if( it == List.end() )

it = List.begin() ;

}

printf("%d ",*it);

tmp = it ;

it++;

if( it == List.end() ) it = List.begin() ;

List.erase(tmp);

}

return ;

}

STL——list

【解法二】

利用STL里面的queue,实现该过程,从队头出来,从队尾插入。

#include

#include

using namespace std;

int main()

{

int n,m;

queue Q;

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

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

Q.push(i);

}

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

int cur = Q.front();

Q.pop();

for(int j=;j

Q.push(cur);

cur = Q.front() ;

Q.pop();

}

printf("%d%c",cur,i==n?'\n':' ');

}

return ;

}

STL—queue

【解法三】

手工实现链表

#include

#include

#include

using namespace std;

const int N = 1e5+;

typedef struct Node{

int val ;

Node * next;

}Node;

Node *head , *tail , *tmp, *p ;

int main()

{

int n,m;

head = new Node ;

head -> next = NULL ;

tail = head ;

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

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

p = new Node ;

p -> val = i ;

p -> next = NULL ;

tail -> next = p ;

tail = p ;

}

p = head -> next ;

tail -> next = head -> next;

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

for(int j=;j

p = p->next;

}

printf("%d ",p->next->val);

tmp = p -> next ;

p -> next = tmp -> next ;

p = p -> next ;

free(tmp);

}

return ;

}

手工链表

【解法四】

手工实现队列

#include

#include

using namespace std;

const int N = 1e5+;

int main()

{

int n,m;

int Head = , Tail = ;

int Q[N];

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

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

Q[++Tail] = i ;

}

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

int cur = Q[Head++];

for(int j=;j

Q[++Tail] = cur ;

cur = Q[Head++];

}

printf("%d%c",cur,i==n?'\n':' ');

}

return ;

}

手工队列

【解法五】

这个题目最正解的做法是权值线段树,权值树状数组。

#include

#include

using namespace std;

const int N=;

int n,m;

struct Stree

{

int l,r;

int dat;

}t[N<

//结构体

//建树

void build(int p,int l,int r)

{

t[p].l=l;t[p].r=r;

if(l==r)

{

t[p].dat=;

//初始化为1,表示这里是有人的

return;

}

int mid=(l+r)>>;

build(p<

build(p<

t[p].dat=t[p<

}

//把 x 踢出去

void change(int p,int x)

{

if(t[p].l==t[p].r)

{

t[p].dat=;

return;

}

int mid=(t[p].l+t[p].r)>>;

if(x<=mid) change(p<

else change(p<

t[p].dat=t[p<

}

//查询 x 的位置

int query(int p,int x)

{

if(t[p].l==t[p].r)

return t[p].l;

//如果左边的剩余位置小于这个编号,那就在右边区域查找左边区域放不下的

if(x>t[p<

else return query(p<

}

int main()

{

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

if(n==) return ;

build(,,n);

int pos=;

while(n)

{

pos=(pos+m-)%t[].dat+;//t[1].dat即剩余总人数

//先给 pos-1, 避免出现mod 完变成0的情况,mod完之后在 +1

//处理位置

// if(pos==0) pos=t[1].dat;

int qwq=query(,pos);

//查寻当前这个人的位置

cout<

//输出

change(,qwq);

//踢出队伍

n--;

}

return ;

}

//By Yfengzi

权值线段树

#include

#include

using namespace std;

const int maxn=3e4+;

int n,m,maxx;

int bit[maxn];

inline int lowbit(int x)

{

return x&-x;

}

inline void add(int pos,int x)

{

for(int i=pos;i<=maxx;i+=lowbit(i))bit[i]+=x;

}

inline int find_kth(int k)

{

int ans=,now=;

for(int i=;i>=;i--)

{

ans+=(<

if(ans>maxx||bit[ans]+now>=k)ans-=(<

else now+=bit[ans];

}

return ans+;

}

int main()

{

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

maxx=n; //这里因为n后面会改变,所以先记录一下n的值。

for(int i=;i<=n;i++)bit[i]=lowbit(i);//这里完全等价于add(i,1),因为一开始都是1,所以bit[i]=i-(i-lowbit(i)+1)+1=lowbit(i)

int now=;//从1开始

while(n)

{

now=(now-+m-)%n+;//这里是小细节,本来的式子应该是(now+m-1)%n的,但是考虑如果只剩下2个元素,而我们当前要找的就是第二个元素呢?直接模就是0了,所以用一个+1 -1 的小操作更改取模运算的值域,这样就可以取到n的值了,而对别的无影响

int ans=find_kth(now);//找kth

add(ans,-);//把这个人删除

printf("%d ",ans);

n--;

}

return ;

}

权值树状数组

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值