zoukankan      html  css  js  c++  java
  • UVA 133“The Dole Queue”(循环报数处理技巧)

    参考资料

      [1]:紫书P82

    •题意(by紫书)

      

      按照被选中的次序输出这 n 个人的编号;

      如果A和B选中的是同一个人,输出一个这个人的编号;

      输出格式:输出的每个编号占3个字节,不够3个字节在前面用空格补;

    •循环报数处理技巧

      n个人按照逆时针顺序编号1~n;

      给你一个整数 k 和 cur;

      cur表示从这 n 个人中任意选取的一个编号;

      k > 0 : 找 cur 左手边的第 k 个人的编号;

      k < 0 : 找 cur 右手边的第 k 个人的编号;

      循环报数问题,需要处理的边界问题是:

        编号 1 的左手边的人的编号为 n;

        编号 n 的右手边的人的编号为 1;

      之前常用的处理的方式为,循环处理,如果 cur 从编号 n 来到编号 n+1,特判,令其等于 1;

      反之,如果 cur 从编号 1 来到编号 0,特判,令其等于 n;

      下面说下一我从紫书上学到的技巧;

    1 pos = (cur + k - 1 + n)%n + 1;
    2 pos : 从cur编号顺时针或逆时针找到的第k个人的编号
    3 k : k > 0,找cur右手边的第k个人的编号,反之找cur左手边的第k个人的编号;
    1 while(~scanf("%d%d%d",&n,&cur,&k))
    2 {
    3     k=k%n;
    4     int pos=(cur+k-1+n)%n+1;
    5     cout<<pos<<endl;
    6 }

    •我的理解

      

      n个人顺时针围城一圈,从 x 位置开始,顺时针找其左(或逆时针找其右)手边的第 k 个人(-n < k < n , k > 0 顺时针找, k < 0 逆时针找);

      假设 k > 0 ,那么第 k 个人的编号为:

      

      合并这两个式子就是

        nextPos = (x+k-1+n)%n+1;

      简单证明这个式子得正确性:

        ①如果 x+k ≤ n,那么 (x+k-1+n)%n+1 = x+k;

        ②如果 x+k > n:

          1)x+k = n+1 : (x+k-1+n)%n+1 = 1;

          2)x+k > n+1 : (x+k-1+n)%n 就是目的编号的前一个编号,+1就等于目的编号;

      那如果 k < 0 呢?

      假设找 x 左手边的第 y 个人的编号 = 找 x 右手边的第 k 个人的编号;

      那么 |k| + y = n,也就是 y = n-|k|;

      带入上式得:

        nextPos = (x+n-|k|-1+n)%n+1;

      即 nextPos = (x-|k|-1+n)%n+1;

      综上,不论 k 是大于0还是小于0,nextPos = (x+k-1+n)%n+1;

    •Code

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define memF(a,b,n) for(int i=0;i <= n;a[i++]=b);
     4 
     5 int n,k,m;
     6 bool vis[30];
     7 
     8 int Go(int cur,int d,int x)
     9 {
    10     /**
    11         d=1:逆时针找第x个人
    12         d=-1:顺时针找第x个人
    13         当前的cur肯定是不满足条件的位置
    14         所以初始 a=n,b=1
    15         之后,a,b的值就是上一次出队的编号
    16     */
    17     while(x--)
    18     {
    19         do
    20         {
    21             cur=(cur+d-1+n)%n+1;
    22         }while(vis[cur]);
    23     }
    24     return cur;
    25 }
    26 void Solve()
    27 {
    28     memF(vis,false,n);
    29 
    30     int a=n,b=1;
    31     int left=n;
    32     while(left--)
    33     {
    34         a=Go(a,1,k);
    35         b=Go(b,-1,m);
    36 
    37         vis[a]=true;
    38         vis[b]=true;
    39 
    40         printf("%3d",a);
    41         if(b != a)
    42         {
    43             left--;
    44             printf("%3d",b);
    45         }
    46         if(left)
    47             printf(",");
    48     }
    49     printf("
    ");
    50 }
    51 int main()
    52 {
    53     while(~scanf("%d%d%d",&n,&k,&m) && n+k+m)
    54         Solve();
    55 
    56     return 0;
    57 }
    View Code
  • 相关阅读:
    [转]用异或交换两个整数的陷阱
    线索化二叉树
    [转]Socket编程中,阻塞与非阻塞的区别
    两个链表的归并
    [转] std::string and stl 算法
    类图
    leetcode 答案
    about raw socket
    54. Spiral Matrix【数组】
    矩阵乘法问题的实现
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/11081905.html
Copyright © 2011-2022 走看看