问题
解释
N
数字系统中的数字集,我们将自然数M
定义为可写数字(WN)来自U
成员的这个数字系统使用每个成员不超过一次。更严格的'书面'定义: - 此处CONCAT
表示连接。N
数字系统中的符号集,我们将自然数M
定义为连续可实现的数字(CAN) U
和M
以及N-1
的号码是U
和M
的CAN号码(另一个定义可能是N
是CAN如果U
和M
的所有0 .. N
个数字都是WN,则U
和M
。更严格: 问题
我们有一组S
自然数:(我们将零视为自然数)和自然数M
,M>1
。问题是找到给定U
和M
的最大CAN(MCAN)。给定集U
可能包含重复 - 但每个副本不能多次使用(即U
包含{x,y,y,z}) - 然后每个y
可以使用0或1次,因此y
可以使用0 ... 2次总计)。同样U
预计在M
- 数字系统中有效(即8
),任何成员中都不能包含符号9
或M=8
。原因是U
的成员是数字,而不是M
的符号(因此11
对{{1}有效}) - 否则问题将是微不足道的。
我的方法
我现在想到一个简单的算法,它只是通过以下方式检查当前号码是否为CAN:
M=10
和0
的{{1}}是否为WN?转到2:我们已经完成,MCAN为空U
和M
的{{1}}是否为WN?转到3:我们完成了,MCAN是1
因此,该算法试图构建所有这些序列。我怀疑这部分可以改进,但可能可以吗?现在,如何检查数字是否为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
符号。
答案 0 :(得分:2)
哈希从0
到m-1
的数字的数字计数。散列由一个重复数字组成的大于m
的数字。
MCAN受最小digit
的约束,无法构造给定digit count
的该数字的所有组合(例如,X000,X00X,X0XX,XX0X,XXX0,XXXX),或{ {0}}在零的情况下(例如,对于四个数字的所有组合,仅需要三个零的组合;对于零计数零,MCAN为空)。数字计数按升序进行评估。
示例:
(digit count - 1)
答案 1 :(得分:2)
我所做的递归步骤是:
这是我的代码:
$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/算法。)唯一的区别是,在我们的例子中,我们只有每个元素的有限数量,而不是无限供应。
我们不会像硬币更改问题那样减去我们正在使用的金额,而是从我们尝试匹配的字符串前面剥离匹配符号。 (这与我们的补充相反 - 连接。)