税务史学家


9

介绍

有一个收税员在管理其王国的税收时遇到了一些麻烦:历史记录已在大火中燃烧。

他想找出从现有资金继承的角度来看可能有多少过去。幸运的是,他的王国很简单。

王国可以用2D布尔矩阵建模,其中l代表已继承货币的O人,而未继承货币的人。例如:

l O l l

O O O l

l O l O

O O O l

(它将始终是一个矩形)

在下一代中,王国变小了(狼很强!)。

下一代看起来像这样,叠加在上一代上(x是下一代后代的占位符)

l O l l
 x x x
O O O l
 x x x
l O l O
 x x x
O O O l

后代将着眼于那些直接身边的祖先(所以左上角x会看到{ lOOO},称为未对齐的矩形街区

如果只有一位祖先继承了金钱,则后代将从他们那里继承金钱。如果一个以上的祖先继承了金钱,他们将争吵不休,后代最终将不会继承金钱。如果没有人继承金钱,则后代将不会继承任何金钱。

(一个以上的后代可以从一个祖先继承)

因此,下一代看起来像:

​
 l l O

 l l O

 l l O
​

挑战

输入项

世代的当前状态,为任何两个不同值的数组的数组,其中内部数组的长度均相同。

例如,对于上面的示例,可能是:

[
  [True, True, False],
  [True, True, False],
  [True, True, False]
]

输出量

一个整数,表示唯一的前几代,其中下一代是输入。

您可以假定答案将始终小于2 ^ 30-1(或1073741823)。

上一代将被称为“原像”,而这一挑战将是计算原像

计分

这是一个 挑战,因此每个提交都将在我的计算机上进行测试,并且花费最少时间的提交将成为赢家。

输入和输出示例

(那里1是继承金钱0的后代,而不是继承金钱的后代)

输入:

[[1, 0, 1],
 [0, 1, 0],
 [1, 0, 1]]

输出:

4

输入:

[[1, 0, 1, 0, 0, 1, 1, 1],
 [1, 0, 1, 0, 0, 0, 1, 0],
 [1, 1, 1, 0, 0, 0, 1, 0],
 [1, 0, 1, 0, 0, 0, 1, 0],
 [1, 0, 1, 0, 0, 1, 1, 1]]

输出:

254

输入:

[[1, 1, 0, 1, 0, 1, 0, 1, 1, 0],
 [1, 1, 0, 0, 0, 0, 1, 1, 1, 0],
 [1, 1, 0, 0, 0, 0, 0, 0, 0, 1],
 [0, 1, 0, 0, 0, 0, 1, 1, 0, 0]]

输出:

11567

6
当我第一次打开页面时,仅看到“ lOOlLOOOOLLlololoLOLOLOOLOLOLOLL”。
魔术章鱼缸

Answers:


4

使用BuDDy库的 C ++

这似乎是使用二进制决策图的不错的借口。王国被转换成一个大的布尔公式,在其中我们必须计算满足它的方式。(听起来)这样做有时可能会更有效率。

王国必须以程序常数的形式给出,并以平面数组和明确给出的尺寸给出。(留给读者的输入是不错的执行方式:-)

这是令人尴尬的简单代码:

#include <iostream>
#include <bdd.h>

// describe the kingdom here:

constexpr int ROWS = 4;
constexpr int COLS = 10;

constexpr int a[] = {
   1, 1, 0, 1, 0, 1, 0, 1, 1, 0,
   1, 1, 0, 0, 0, 0, 1, 1, 1, 0,
   1, 1, 0, 0, 0, 0, 0, 0, 0, 1,
   0, 1, 0, 0, 0, 0, 1, 1, 0, 0,
};

// end of description

// check dimensions
static_assert(ROWS*COLS*sizeof(int)==sizeof(a),
          "ROWS*COLS must be the number of entries of a");

// dimensions of previous generation
constexpr int R1 = ROWS+1;
constexpr int C1 = COLS+1;

// condition that exactly one is true
bdd one(bdd a, bdd b, bdd c, bdd d){
  bdd q = a & !b & !c & !d;
  q |= !a & b & !c & !d;
  q |= !a & !b & c & !d;
  q |= !a & !b & !c & d;
  return q;
}

int main()
{
  bdd_init(1000000, 10000); // tuneable, but not too important
  bdd_setvarnum(R1*C1);
  bdd q { bddtrue };
  for(int j=COLS-1; j>=0; j--) // handle high vars first
    for (int i=ROWS-1; i>=0; i--){
      int x=i+R1*j;
      bdd p=one(bdd_ithvar(x), bdd_ithvar(x+1),
                bdd_ithvar(x+R1), bdd_ithvar(x+R1+1));
      if (!a[COLS*i+j])
        p = !p;
      q &= p;
    }
  std::cout << "There are " << bdd_satcount(q) << " preimages\n";
  bdd_done();
}

要使用debian 8(jessie)进行编译,请安装libbdd-dev并执行g++ -std=c++11 -o hist hist.cpp -lbdd。(优化选项几乎没有区别,因为实际工作是在库中完成的。)

大量示例可能导致出现有关垃圾回收的消息。他们可能会被压制,但我更愿意看到它们。

bdd_satcount返回一个double,但对于预期结果范围已经足够。对于精确的(大)整数,可以使用相同的计数技术。

该代码针对进行了优化ROWS<COLS。如果行多于列,则转置矩阵可能是一个好主意。


2.39秒。这是我的一半时间!将此标记为已接受。
Artyer

1
@Artyer:您是否愿意发布运行时间最长的隐藏测试用例?以及您的解决方案(如果需要)。
安德鲁·爱泼斯坦

@AndrewEpstein我最近发生了硬盘故障,丢失了代码和原始测试用例(有数百个,我认为最大为300宽,10高)。抱歉。
Artyer

3

Python 2.7

这只是天真的第一次尝试。这不是特别快,但它是正确的。

第一个观察结果是,每个单元格都依赖于上一代中的四个单元格。我们可以将这四个单元格表示为4位数字(0-15)。根据规则,如果上一代中恰好有一个相邻小区是1,则当前一代中的给定小区将为1,否则为0。那些对应于两个的幂,即[1, 2, 4, 8]。当四个祖先表示为4位数字时,任何其他数字将导致0当前代中的a 。有了这些信息,在看到当前一代中的一个单元时,我们可以将上一代中的邻居的可能性分别缩小到四种可能性之一或十二种可能性之一。

我选择代表邻居如下:

32
10

其中0是最低有效位,依此类推。

第二个观察结果是,对于当前一代中的两个相邻单元,上一代中的两个邻域重叠:

32  32
10  10

要么:

32
10

32
10

在水平情况下,2左邻域与3右邻域重叠,并且相似0于左和1右。在垂直情况下,1顶部邻域与3底部邻域重叠,并且与0顶部和2底部相似。

这种重叠意味着我们可以根据我们已经选择的内容来缩小尚未选择的社区的可能性。该代码以递归的深度优先搜索可能的原像的方式从左到右,从上到下进行工作。下图说明了在查看当前单元格的可能邻域时必须考虑的先前邻域:

f = free choice
h = only have to look at the neighborhood to the left
v = only have to look at the neighborhood to the top
b = have to look at both left and top neighborhoods

[f, h, h, h, h],
[v, b, b, b, b],
[v, b, b, b, b],
[v, b, b, b, b]

这是代码:

def good_horizontal(left, right):
    if (left & 4) >> 2 != (right & 8) >> 3:
        return False
    if left & 1 != (right & 2) >> 1:
        return False
    return True


def good_vertical(bottom, top):
    if (bottom & 8) >> 3 != (top & 2) >> 1:
        return False
    if (bottom & 4) >> 2 != (top & 1):
        return False
    return True


ones = [1, 2, 4, 8]
zeros = [0, 3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15]
h = {}
v = {}

for i in range(16):
    h[i] = [j for j in range(16) if good_horizontal(i, j)]
    v[i] = [j for j in range(16) if good_vertical(i, j)]


def solve(arr):
    height = len(arr)
    width = len(arr[0])

    if height == 1 and width == 1:
        if arr[0][0] == 1:
            return 4
        else:
            return 12
    return solve_helper(arr)


def solve_helper(arr, i=0, j=0, partial=None):
    height = len(arr)
    width = len(arr[0])

    if arr[i][j] == 1:
        poss = ones
    else:
        poss = zeros

    if i == height - 1 and j == width - 1:  # We made it to the end of this chain
        if height == 1:
            return sum([1 for p in poss if p in h[partial[-1][-1]]])
        else:
            return sum([1 for p in poss if partial[-2][-1] in v[p] and p in h[partial[-1][-1]]])

    if j == width - 1:
        new_i, new_j = i + 1, 0
    else:
        new_i, new_j = i, j + 1

    if i == 0:
        if j == 0:
            # first call
            return sum([solve_helper(arr, new_i, new_j, [[p]]) for p in poss])
        # still in the first row
        return sum([solve_helper(arr, new_i, new_j, [partial[0] + [p]]) for p in poss if p in h[partial[0][-1]]])
    if j == 0:  # starting a new row
        return sum([solve_helper(arr, new_i, new_j, [r for r in partial + [[p]]]) for p in poss if partial[i - 1][0] in v[p]])
    return sum([solve_helper(arr, new_i, new_j, [r for r in partial[:-1] + ([partial[-1] + [p]])]) for p in poss if p in h[partial[i][-1]] and partial[i - 1][j] in v[p]])

要运行它:

test3 = [
    [1, 1, 0, 1, 0, 1, 0, 1, 1, 0],
    [1, 1, 0, 0, 0, 0, 1, 1, 1, 0],
    [1, 1, 0, 0, 0, 0, 0, 0, 0, 1],
    [0, 1, 0, 0, 0, 0, 1, 1, 0, 0]
]

expected3 = 11567

assert(solve(test3) == expected3)

1
这需要很长时间才能完成隐藏的测试用例,因此我没有对该提交进行评分。尝试使用不同的算法,因为这样的时间复杂度太高了(O(<something>^n)我认为是这样)
。– Artyer
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.