11

问题

定义

  • 让我们将自然数定义为数字系统中设置的数字N可写数(WN),如果它可以从使用每个成员不超过一次的成员中写入该数字系统。'书面'的更严格定义:- 这里意味着连接。在此处输入图像描述MU在此处输入图像描述CONCAT
  • 让我们将自然数定义为数字系统中符号集N连续可实现数(CAN),如果它是 and 的 WN 数并且也是 and的CAN 数(另一个定义可能是 CAN并且如果所有数字都是WN和)。更严格:在此处输入图像描述MUMN-1UMNUM0 .. NUM在此处输入图像描述

问题

让我们有一组S自然数:(在此处输入图像描述我们将零视为自然数)和自然数M, M>1。问题是找到给定的最大 CAN (MCAN)UM。给定的集合U 可能包含重复项- 但每个重复项不能多次使用,原因(即如果U包含 {x, y, y, z} - 那么每个重复项y可以使用 0 次或 1 次,因此y可以使用 0..共 2 次)。也U期望在M-numeral 系统中有效(即不能包含符号89任何成员 if M=8)。而且,当然,成员U数字,而不是符号M(所以对11M=10) - 否则问题将是微不足道的。

我的方法

我现在想到了一个简单的算法,它只是通过以下方式检查当前号码是否为 CAN:

  1. 检查0给定的 WNUM? 转到 2:我们完成了,MCAN 为空
  2. 检查1给定的 WNUM? 转到 3:我们完成了,MCAN 是0
  3. ...

因此,该算法正在尝试构建所有这些序列。我怀疑这部分可以改进,但可能吗?现在,如何检查号码是否为 WN。这也是某种“替代蛮力”。我已经意识到M=10(事实上,因为我们正在处理字符串,任何其他M都不是问题)PHP函数:

//$mNumber is our N, $rgNumbers is our U
function isWriteable($mNumber, $rgNumbers)
{
   if(in_array((string)$mNumber, $rgNumbers=array_map('strval', $rgNumbers), true))
   {
      return true;
   }
   for($i=1; $i<=strlen((string)$mNumber); $i++)
   {
      foreach($rgKeys = array_keys(array_filter($rgNumbers, function($sX) use ($mNumber, $i)
      {
         return $sX==substr((string)$mNumber, 0, $i);
      })) as $iKey)
      {
         $rgTemp = $rgNumbers;
         unset($rgTemp[$iKey]);
         if(isWriteable(substr((string)$mNumber, $i), $rgTemp))
         {
            return true;
         }
      }
   }
   return false;
}

- 所以我们正在尝试一件,然后检查其余部分是否可以用递归来编写。如果它不能被写入,我们正在尝试下一个成员U。我认为这是可以改进的一点。

细节

如您所见,一种算法正在尝试构建之前的所有数字N并检查它们是否为 WN。但唯一的问题是 - 找到 MCAN,所以,问题是:

  • 可能是建设性算法在这里过度?如果是,还有哪些其他选项可以使用?
  • 是否有更快速的方法来确定给定的数字是否为 WNUM?(如果前一点有肯定的答案,这一点可能没有意义,我们不会建立和检查之前的所有数字N)。

样品

U = {4, 1, 5, 2, 0}
M = 10

然后 MCAN = 2(无法达到 3)

U = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11}
M = 10

然后 MCAN = 21(之前都可以达到,因为总共22没有两个2符号)。

4

3 回答 3

2

0散列从到的数字的位数m-1。散列大于m由一个重复数字组成的数字。

MCAN 受限于不能构造digit给定数字的所有组合的最小值(例如,X000,X00X,X0XX,XX0X,XXX0,XXXX),或者在零的情况下(例如,对于四个的所有组合)数字,只有三个零需要组合;对于零计数,MCAN 为空)。数字计数按升序计算。digit count(digit count - 1)

例子:

1. MCAN (10, {4, 1, 5, 2, 0})
   3 is the smallest digit for which a digit-count of one cannot be constructed.
   MCAN = 2

2. MCAN (10, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11})
   2 is the smallest digit for which a digit-count of two cannot be constructed.
   MCAN = 21

3. (from Alma Do Mundo's comment below) MCAN (2, {0,0,0,1,1,1})
   1 is the smallest digit for which all combinations for a digit-count of four
   cannot be constructed.
   MCAN = 1110

4. (example from No One in Particular's answer) 
   MCAN (2, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1111,11111111})
   1 is the smallest digit for which all combinations for a digit-count of five
   cannot be constructed.
   MCAN = 10101
于 2013-09-20T13:30:04.747 回答
2

我所做的递归步骤是:

  1. 如果数字字符串在您的字母表中可用,请将其标记为已使用并立即返回
  2. 如果数字字符串的长度为 1,则返回失败
  3. 将字符串分成两部分并尝试每个部分

这是我的代码:

$u = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11];

echo ncan($u), "\n"; // 21

// the functions

function satisfy($n, array $u)
{
        if (!empty($u[$n])) { // step 1
                --$u[$n];
                return $u;
        } elseif (strlen($n) == 1) { // step 2
                return false;
        }

        // step 3
        for ($i = 1; $i < strlen($n); ++$i) {
                $u2 = satisfy(substr($n, 0, $i), $u);
                if ($u2 && satisfy(substr($n, $i), $u2)) {
                        return true;
                }
        }

        return false;
}

function is_can($n, $u)
{
        return satisfy($n, $u) !== false;
}

function ncan($u)
{
        $umap = array_reduce($u, function(&$result, $item) {
                @$result[$item]++;
                return $result;
        }, []);
        $i = -1;

        while (is_can($i + 1, $umap)) {
                ++$i;
        }

        return $i;
}
于 2013-09-21T01:58:25.390 回答
1

这是另一种方法:

1) 根据基数 M 的通常数字排序对集合 U 进行排序。
2) 如果在 0 和 (M-1) 之间缺少一个符号,那么这是第一个不是 MCAN 的数字。
3) 找到集合 U 中条目数最少的第一个符号。由此我们得到第一个数字的上限,它不是 MCAN。该数字将是 {xxxx} N 次。例如,如果 M = 4 且 U = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3},则数字 333 不是 MCAN。这给了我们我们的上限。
4) 所以,如果集合 U 中出现次数较少的第一个元素是 x,并且出现次数为 C,那么我们可以用 C 位清楚地表示任何数字。(因为每个元素至少有 C 个条目)。
5) 现在我们问是否有任何小于 (C+1)x 的数不能是 MCAN?好吧,任何(C+1)位数字都可以有(C+1)个相同的符号,或者最多只有(C)个相同的符号。由于从步骤 3 开始 x 是最小的,因此可以完成 y < x 的 (C+1)y 并且可以为任何不同的 a、b 完成 (C)a + b,因为它们至少具有 (C) 个副本。

上述方法适用于只有 1 个符号的集合元素。但是,我们现在看到,如果允许使用多符号元素,它会变得更加复杂。考虑以下情况:

U = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1111,11111111}

定义 c(A,B) = 'B' 长度的 'A' 符号的数量。

因此对于我们的示例,c(0,1) = 15, c(0,2) = 0, c(0,3) = 0, c(0,4) = 0, ... c(1,1) = 3, c(1,2) = 0, c(1,3) = 0, c(1,4) = 1, c(0,5) = 0, ..., c(1,8) = 1

我们不能做的最大0字符串是16。我们不能做的最大1字符串也是16。1
= 1
11 = 1+1
111 = 1+1+1
1111 = 1111
11111 = 1+1111
111111 = 1+1+1111
1111111 = 1+1+1+1+1+ 1111111111111111111111111111111111111
=
1 + 111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 往 11111111111111 = 1+1+1111+11111111 111111111111111 = 1+1+1+1111+11111111





但是我们可以将字符串设为 11111101111 吗?我们不能,因为最后 1 个字符串 (1111) 需要唯一的一组 1,其中连续 4 个。一旦我们接受它,我们就无法制作第一个 1 字符串(111111),因为我们只有一个 8(太大)或 3 个太小的 1 长度。

所以对于多符号,我们需要另一种方法。

通过对字符串进行排序和排序,我们知道对于给定符号我们不能做的最小长度是多少。(在上面的示例中,它将是 16 个 0 或 16 个 1。)所以这是我们的答案上限。

我们现在要做的是开始一个 1 并以 M 为底数。对于每个数字,我们以 M 为底数写它,然后确定我们是否可以从我们的集合 U 中得到它。我们使用与硬币找零问题:动态规划。(例如http://www.geeksforgeeks.org/dynamic-programming-set-7-coin-change/的算法。)唯一的区别是,在我们的例子中,我们只有有限数量的每个元素,而不是无限供应。

我们没有像在硬币找零问题中那样减去我们使用的金额,而是从我们试图匹配的字符串的前面去除匹配符号。(这与我们的加法相反 - 连接。)

于 2013-09-21T15:05:25.333 回答