在不使用结构的情况下冒泡某种条件?

Posted

技术标签:

【中文标题】在不使用结构的情况下冒泡某种条件?【英文标题】:Bubbling up some sort of condition without using a structure? 【发布时间】:2019-02-21 07:28:02 【问题描述】:

有一场比赛。玩家由整数标识,并以与他们相邻的整数的锦标赛风格进行比赛。所以,1 播放 2,3 播放 4,5 播放 6,7 播放 8。然后,1/2 播放 3/4,5/6 播放 7/8,然后 1/2/3/4 播放 5/6/ 7/8。

示例输入:

8 5
1 2 3 4 5

第一行 = 第一个数字是锦标赛中的玩家数量(总是一些 2^N 的数字),第二个数字是在锦标赛开始前退出的玩家数量

第二行=退出玩家的ID

如果玩家自动进入下一轮,因为他们本来要玩的人退出(称之为 BYE),增加计数。最后输出计数。

所以样本输入的输出将是 2(6 在第一轮自动晋级,因为 6 应该与 5 退出,最终在倒数第二轮,6/7/8 自动晋级) .

我正在考虑要么将所有内容都保存在树中(但这不会真的很有效吗?),或者只是在你去的时候进行解析,然后冒泡 BYE。第二种解决方案的问题是我不知道如何考虑/存储关系,即谁将扮演谁,以及在实施中你如何在没有结构的情况下冒泡。

【问题讨论】:

这里的任务是什么,创建整棵树还是只数再见? 计数再见。我只想打印正确的输出。 必须在不使用数据结构存储数据的情况下解决任务? 不,可以使用结构体。 N 可以有多大? 【参考方案1】:

您可以使用一个简单的数组(大小为 2^N)。将您的参与者编码为 0 代表缺席,1 代表出席,并模拟比赛。在索引2*k 的每一轮中,玩家将与索引2*k + 1 对战,“获胜者”被移动到索引k。再见条件是 XOR,“赢家”是 OR。在伪代码中,

    while player_count > 1
        for k in range (player_count / 2)
            byes += arr[2*k] ^ arr[2*k + 1]
            arr[k] = arr[2*k] | arr[2*k + 1]
        player_count /= 2

空间和时间都与玩家数量呈线性关系。

【讨论】:

@J.Doe 零和一,根据玩家的存在。【参考方案2】:

没有结构就不能存储任何东西,但是某些时间结构可以是隐式的,例如在递归的情况下,我们有没有明确声明的堆栈。

递归解法示例:

public class App 
    public static void main(String[] args) 
        // I use 0 based indexing here
        Set<Integer> absent = new HashSet<>(Arrays.asList(0, 1, 2, 3, 4));

        int count = rec(absent, 8, 0);
        System.out.println("count = " + count);
    

    private static int rec(Set<Integer> absent, int currentGroupSize, int start) 
        if (currentGroupSize == 1) 
            return absent.contains(start) ? -1 : 0;
        

        int r1 = rec(absent, currentGroupSize / 2, start);
        int r2 = rec(absent, currentGroupSize / 2, start + currentGroupSize / 2);
        if (r1 == -1) 
            if (r2 == -1) 
                return -1;
             else 
                return r2 + 1;
            
         else 
            if (r2 == -1) 
                return r1 + 1;
             else 
                return r1 + r2;
            
        
    

【讨论】:

我添加了示例。【参考方案3】:

这是时间O(R log R) 和空间O(R) 的解决方案,其中R 是退役(退出)球员的数量。如果退役球员的 ID 是按升序排列的,那么您的最后一个见解是正确的:您可以读取 ID 并将它们冒泡到O(R) 时间和O(1) 内存中。当N 远大于R(例如数十亿)时,这会有所帮助,因为这排除了存储任何大小为N 的数组。

从概念上讲,退役球员 ID 是树上的叶子。这是N = 8 的树。我从所有 ID 中减去 1,因为这被证明是一个黑客问题,黑客喜欢从 0 开始计数。:-)

           0-7
        /       \
     0-3         4-7
   /     \     /     \
 0-1    2-3   4-5    6-7
 / \    / \   / \    / \
0   1  2   3 4   5  6   7

我们的想法是查看输入中的紧凑范围并计算出它们产生了多少字节。例如,范围 [0-3] 产生一个轮空:左括号(子树)中没有比赛,从 [4-7] 范围进入决赛的人将在决赛中轮空。范围 [4-7] 也是如此。基本上,如果一个范围覆盖一棵完整的子树,那就是再见。请注意,我们寻找最大的完整子树,例如[0-3] 而不是 [0-1] + [2-3] 分开。

[0-4] 呢?我们需要将范围分成 [0-3] 和 [4-4]。然后 [4-4] 是第二个子树(只有一个节点的普通子树),这意味着玩家 5 也将获得再见。所以 [0-4] 算作两个字节。我们通过计算数字 5(范围的大小)中的位来确定这一点。由于 5 = 1012,我们得到答案 2。这是 bit hack 部分,我将略过,但如果需要可以扩展。

最后一个要考虑的问题是限制范围大小。设N=1024 并考虑范围[4-100]。从 4 开始,子树在 7 处填满,此时我们应该处理范围 [4-7](并得到 1 bye),然后从 8 继续(该子树将依次在 15 处填充,依此类推)。计算右端也涉及到一些技巧。考虑起点 40=1010002。子树的大小由最低有效位给出,即 10002=8,所以我们应该在范围 [40-47] 之后中断。同样,我将掩盖细节,但如果需要可以扩展。

C 代码很短(很抱歉没有写 Java,已经有一段时间了)。为简洁起见,它使用 GCC 的 built-in popcount function 来计算位数,但有 many other methods 可用。同样,限制范围大小涉及finding the rightmost set bit。

#include <stdio.h>

void startRange(unsigned p, unsigned* start, unsigned* end, unsigned* limit) 
  *start = *end = p;
  *limit = p + (p & -p) - 1;
  printf("started range %u limit %u\n", p, *limit);


int processRange(unsigned start, unsigned end) 
  printf("processing range [%u,%u]\n", start, end);
  return __builtin_popcount(end - start + 1);


int main() 
  unsigned n, r, p, result;
  unsigned start, end, limit;

  /* read from standard input */
  scanf("%d%d%d", &n, &r, &p);
  startRange(p - 1, &start, &end, &limit);
  result = 0;

  while (--r)  /* read r-1 more numbers */
    scanf("%d", &p);
    p--;

    if (p == end + 1 && p <= limit) 
      /* continue while we have consecutive numbers, but not past the limit */
      end++;
     else 
      /* close the previous range and start a new one at p */
      result += processRange(start, end);
      startRange(p, &start, &end, &limit);
    
  

  /* close any outstanding range we have */
  result += processRange(start, end);
  printf("%d\n", result);

  return 0;

【讨论】:

以上是关于在不使用结构的情况下冒泡某种条件?的主要内容,如果未能解决你的问题,请参考以下文章

数据结构-排序算法总结

如何在不知道名称的情况下访问结构成员?

如何在不使用 SQL 中的多个连接条件的情况下获取同一行中的所有值?

如何在不更改链接结构的情况下使用 \ 转义字符 (、)、[、]、*、_、:[]()

我可以在不使用点运算符的情况下访问结构内部的结构吗?

在不使结构只读的情况下避免使用带有结构的“in”对性能造成的影响?