最大连续可达到的数量

时间:2013-09-20 10:08:22

标签: algorithm numbers

问题

解释

  • 如果可以写入N数字系统中的数字集enter image description here,我们将自然数M定义为可写数字(WN)来自U成员的这个数字系统使用每个成员不超过一次。更严格的'书面'定义:enter image description here - 此处CONCAT表示连接。
  • 如果是{WN-,则N数字系统中的符号集enter image description here,我们将自然数M定义为连续可实现的数字(CAN) UM以及N-1的号码是UM的CAN号码(另一个定义可能是N是CAN如果UM的所有0 .. N个数字都是WN,则UM。更严格:enter image description here

问题

我们有一组S自然数:enter image description here(我们将零视为自然数)和自然数MM>1。问题是找到给定UM的最大CAN(MCAN)。给定集U 可能包含重复 - 但每个副本不能多次使用(即U包含{x,y,y,z}) - 然后每个y可以使用0或1次,因此y可以使用0 ... 2次总计)。同样U预计在M - 数字系统中有效(即8),任何成员中都不能包含符号9M=8。原因是U的成员是数字,而不是M符号(因此11对{{1}有效}) - 否则问题将是微不足道的。

我的方法

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

  1. 检查给定M=100的{​​{1}}是否为WN?转到2:我们已经完成,MCAN为空
  2. 检查给定UM的{​​{1}}是否为WN?转到3:我们完成了,MCAN是1
  3. ...
  4. 因此,该算法试图构建所有这些序列。我怀疑这部分可以改进,但可能可以吗?现在,如何检查数字是否为WN。这也是某种“替代蛮力”。我已经意识到U(事实上,因为我们处理字符串,任何其他M都不是问题)PHP函数:

    0

    - 我们正在尝试一件,然后检查其余部分是否可以用递归写入。如果无法写入,我们正在尝试M=10的下一个成员。我认为这是可以改进的一点。

    具体细节信息

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

    • 可能是建设性的算法在这里过度?并且,如果是,可以使用哪些其他选项?
    • 是否有更快捷的方法来确定给定//$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的号码是否为WN? (如果前一点有正面答案,我们就不会构建并检查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(之前都可以到达,U总共没有两个M符号。

3 个答案:

答案 0 :(得分:2)

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

MCAN受最小digit的约束,无法构造给定digit count的该数字的所有组合(例如,X000,X00X,X0XX,XX0X,XXX0,XXXX),或{ {0}}在零的情况下(例如,对于四个数字的所有组合,仅需要三个零的组合;对于零计数零,MCAN为空)。数字计数按升序进行评估。

示例:

(digit count - 1)

答案 1 :(得分:2)

我所做的递归步骤是:

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

    $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;
    }
    

答案 2 :(得分: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)。由于x从步骤3开始最小,因此对于y <1,(C + 1)y。 x可以完成,并且(C)a + b可以对任何不同的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 + 1111
11111111 = 11111111
111111111 = 1 + 11111111
1111111111 = 1 + 1 + 11111111
11111111111 = 1 + 1 + 1 + 11111111
111111111111 = 1111 + 11111111
1111111111111 = 1 + 1111 + 11111111
11111111111111 = 1 + 1 + 1111 + 11111111
111111111111111 = 1 + 1 + 1 + 1111 + 11111111

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

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

我们知道从排序和排序字符串开始,给定符号的最小长度是多少。 (在上面的例子中,它将是16个零或16个。)所以这是我们答案的上限。

我们现在必须做的是开始1并在基数M中计数。对于每个数字,我们将其写在基数M中,然后确定我们是否可以从我们的集合U中创建它。我们通过使用相同的方法来实现用于硬币更换问题:动态编程。 (例如,参见http://www.geeksforgeeks.org/dynamic-programming-set-7-coin-change/算法。)唯一的区别是,在我们的例子中,我们只有每个元素的有限数量,而不是无限供应。

我们不会像硬币更改问题那样减去我们正在使用的金额,而是从我们尝试匹配的字符串前面剥离匹配符号。 (这与我们的补充相反 - 连接。)