zoukankan      html  css  js  c++  java
  • 【数据结构】P1996 约瑟夫问题

    【题目链接】

    https://www.luogu.org/problem/P1996

    题目描述

    n个人(n<=100)围成一圈,从第一个人开始报数,数到m的人出列,再由下一个人重新从1开始报数,数到m的人再出圈,……

    依次类推,直到所有的人都出圈,请输出依次出圈人的编号.

    输入格式

    n m

    输出格式

    出圈的编号

    输入输出样例

    输入 #1
    10 3
    输出 #1
    3 6 9 2 7 1 8 5 10 4

    说明/提示

    100 ≤ m,n ≤ 100


    【题解】

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

    给出一题五解的做法。

    【解法一】

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

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 int n,m;
     5 
     6 int main()
     7 {
     8     list<int> List ;
     9     scanf("%d%d",&n,&m);
    10     for(int i=1;i<=n;i++) List.push_back(i);
    11     list<int> ::iterator it = List.begin(),tmp;
    12 
    13     for(int i=1;i<=n;i++){
    14         for(int j=0;j<m-1;j++){
    15             it++;
    16             if( it == List.end() )
    17                 it = List.begin() ;
    18 
    19         }
    20 
    21         printf("%d ",*it);
    22         tmp = it ;
    23         it++;
    24         if( it == List.end() )  it = List.begin() ;
    25         List.erase(tmp);
    26     }
    27     return 0;
    28 }
    STL——list

    【解法二】

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

     1 #include<cstdio>
     2 #include<queue>
     3 using namespace std;
     4 int main()
     5 {
     6     int n,m;
     7     queue<int> Q;
     8     scanf("%d%d",&n,&m);
     9     for(int i=1;i<=n;i++){
    10         Q.push(i);
    11     }
    12     for(int i=1;i<=n;i++){
    13         int cur = Q.front();
    14         Q.pop();
    15         for(int j=0;j<m-1;j++){
    16 
    17             Q.push(cur);
    18             cur = Q.front() ;
    19             Q.pop();
    20         }
    21         printf("%d%c",cur,i==n?'
    ':' ');
    22     }
    23     return 0;
    24 }
    STL—queue

    【解法三】

    手工实现链表

     1 #include<cstdio>
     2 #include<queue>
     3 #include<cstdlib>
     4 using namespace std;
     5 const int N = 1e5+10;
     6 
     7 typedef struct Node{
     8     int val ;
     9     Node * next;
    10 }Node;
    11 Node *head , *tail , *tmp, *p ;
    12 
    13 int main()
    14 {
    15     int n,m;
    16     head = new Node ;
    17     head -> next = NULL ;
    18     tail = head ;
    19     scanf("%d%d",&n,&m);
    20     for(int i=1;i<=n;i++){
    21         p = new Node ;
    22         p -> val = i ;
    23         p -> next = NULL ;
    24         tail -> next = p ;
    25         tail = p ;
    26     }
    27 
    28     p = head -> next ;
    29     tail -> next = head -> next;
    30 
    31     for(int i=1;i<=n;i++){
    32 
    33         for(int j=0;j<m-2;j++){
    34             p = p->next;
    35         }
    36         printf("%d ",p->next->val);
    37         tmp = p -> next ;
    38         p -> next = tmp -> next ;
    39         p = p -> next ;
    40         free(tmp);
    41     }
    42     return 0;
    43 }
    手工链表

    【解法四】

    手工实现队列

     1 #include<cstdio>
     2 #include<queue>
     3 using namespace std;
     4 const int N = 1e5+10;
     5 int main()
     6 {
     7     int n,m;
     8     int Head = 1 , Tail = 0 ;
     9     int Q[N];
    10     scanf("%d%d",&n,&m);
    11     for(int i=1;i<=n;i++){
    12         Q[++Tail] = i ;
    13     }
    14     for(int i=1;i<=n;i++){
    15         int cur = Q[Head++];
    16         for(int j=0;j<m-1;j++){
    17             Q[++Tail] = cur ;
    18             cur = Q[Head++];
    19         }
    20         printf("%d%c",cur,i==n?'
    ':' ');
    21     }
    22     return 0;
    23 }
    手工队列

    【解法五】

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

     1 #include<iostream>
     2 #include<cstdio>
     3 
     4 using namespace std;
     5 
     6 const int N=100;
     7 
     8 int n,m;
     9 
    10 struct Stree
    11 {
    12     int l,r;
    13     int dat;
    14 }t[N<<2];
    15 //结构体 
    16 
    17 //建树 
    18 void build(int p,int l,int r)
    19 {
    20     t[p].l=l;t[p].r=r;
    21     if(l==r)
    22     {
    23         t[p].dat=1;
    24         //初始化为1,表示这里是有人的 
    25         return;
    26     }
    27     int mid=(l+r)>>1;
    28     build(p<<1,l,mid);
    29     build(p<<1|1,mid+1,r);
    30     t[p].dat=t[p<<1].dat+t[p<<1|1].dat; 
    31 } 
    32 
    33 //把 x 踢出去 
    34 void change(int p,int x)
    35 {
    36     if(t[p].l==t[p].r)
    37     {
    38         t[p].dat=0;
    39         return;
    40     }
    41     int mid=(t[p].l+t[p].r)>>1;
    42     if(x<=mid) change(p<<1,x);
    43     else change(p<<1|1,x);
    44     t[p].dat=t[p<<1].dat+t[p<<1|1].dat;
    45 }
    46 
    47 //查询 x 的位置 
    48 int query(int p,int x)
    49 {
    50     if(t[p].l==t[p].r)
    51         return t[p].l;
    52     //如果左边的剩余位置小于这个编号,那就在右边区域查找左边区域放不下的 
    53     if(x>t[p<<1].dat) return query(p<<1|1,x-t[p<<1].dat);
    54     else return query(p<<1,x);
    55 }
    56 
    57 int main()
    58 {
    59     scanf("%d%d",&n,&m); 
    60     if(n==0) return 0;
    61     build(1,1,n);
    62     int pos=1;
    63     while(n)
    64     {
    65         pos=(pos+m-2)%t[1].dat+1;//t[1].dat即剩余总人数 
    66         //先给 pos-1, 避免出现mod 完变成0的情况,mod完之后在 +1 
    67         //处理位置 
    68 //      if(pos==0) pos=t[1].dat;
    69         int qwq=query(1,pos);
    70         //查寻当前这个人的位置 
    71         cout<<qwq<<" ";
    72         //输出 
    73         change(1,qwq);
    74         //踢出队伍 
    75         n--;
    76     }
    77 
    78     return 0;
    79  }
    80  //By Yfengzi
    权值线段树
     1 #include<iostream>
     2 #include<cstdio>
     3 using namespace std;
     4 
     5 const int maxn=3e4+10;
     6 int n,m,maxx;
     7 int bit[maxn];
     8 
     9 inline int lowbit(int x)
    10 {
    11     return x&-x;
    12 }
    13 inline void add(int pos,int x)
    14 {
    15     for(int i=pos;i<=maxx;i+=lowbit(i))bit[i]+=x;
    16 }
    17 inline int find_kth(int k)
    18 {
    19     int ans=0,now=0;
    20     for(int i=15;i>=0;i--)
    21     {
    22         ans+=(1<<i);
    23         if(ans>maxx||bit[ans]+now>=k)ans-=(1<<i);
    24         else now+=bit[ans];
    25     }
    26     return ans+1;
    27 }
    28 
    29 int main()
    30 {
    31     scanf("%d %d",&n,&m);
    32     maxx=n;     //这里因为n后面会改变,所以先记录一下n的值。 
    33     for(int i=1;i<=n;i++)bit[i]=lowbit(i);//这里完全等价于add(i,1),因为一开始都是1,所以bit[i]=i-(i-lowbit(i)+1)+1=lowbit(i) 
    34     int now=1;//从1开始 
    35     while(n)
    36     {
    37         now=(now-1+m-1)%n+1;//这里是小细节,本来的式子应该是(now+m-1)%n的,但是考虑如果只剩下2个元素,而我们当前要找的就是第二个元素呢?直接模就是0了,所以用一个+1 -1 的小操作更改取模运算的值域,这样就可以取到n的值了,而对别的无影响 
    38         int ans=find_kth(now);//找kth 
    39         add(ans,-1);//把这个人删除 
    40         printf("%d ",ans);
    41         n--;
    42     }
    43     return 0;
    44 }
    权值树状数组
  • 相关阅读:
    面向对象三大特征之多态——Java笔记(七)
    面向对象三大特征之继承(extends)——Java笔记(六)
    this、访问修饰符——Java笔记(五)
    面向对象三大特征之封装与static——(Java学习笔记四)
    初识Java——(Java学习笔记一)
    HTTP/3 简介
    iis 500.19错误解决过程记录
    排序陷阱 List.Sort Linq.OrderBy
    锁的封装 读写锁、lock
    时间“Thu Aug 14 2014 14:28:06 GMT+0800”的转换
  • 原文地址:https://www.cnblogs.com/Osea/p/11371750.html
Copyright © 2011-2022 走看看