세금 역사가


9

소개

그의 왕국의 세금을 관리하는 데 어려움을 겪는 세금 징수 원이 있습니다.

그는 현재 돈이 어디에서 물려 받았는지에 대해 얼마나 많은 과거가있을 수 있는지 알고 싶어합니다. 다행히 그의 왕국은 매우 간단합니다.

왕국은 l돈을 물려받은 O사람을 나타내며 그렇지 않은 사람을 나타내는 2D 부울 행렬로 모델링 할 수 있습니다 . 예를 들면 다음과 같습니다.

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볼 수 { l, O, O, O},라는 정렬되지 않은 사각 지역 )

한 조상 만이 돈을 상속받은 경우, 후손은 돈을 상속받습니다. 두 명 이상의 조상이 돈을 물려 받았다면 그들은 물러서서 후손은 돈을 물려받지 못할 것입니다. 돈을 상속받지 못한 사람은 후손이 돈을 상속받지 않습니다.

(하나 이상의 조상으로부터 하나 이상의 후손이 상속 될 수 있음)

따라서 다음 세대는 다음과 같습니다.

​
 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"은 페이지를 처음 열었을 때 본 것입니다.
Magic Octopus Urn

답변:


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

파이썬 2.7

이것은 순진한 첫 번째 시도입니다. 빠르지는 않지만 정확합니다.

첫 번째 관찰은 각 셀이 이전 세대의 정확히 4 개의 셀에 의존한다는 것입니다. 이 4 개의 셀을 4 비트 숫자 (0-15)로 나타낼 수 있습니다. 규칙에 따르면, 이전 세대에서 정확히 하나의 인접 셀이 1이면 현재 세대의 지정된 셀은 1이고 그렇지 않으면 그렇지 않습니다 0. 그것들은 두 가지의 힘, 즉 [1, 2, 4, 8]. 4 개의 조상이 4 비트 숫자로 표시 될 때, 다른 숫자는 0현재 세대에서 발생합니다. 이 정보를 통해 현재 세대의 셀을 볼 때 이전 세대의 이웃 가능성을 각각 4 개 또는 12 개 중 하나로 좁힐 수 있습니다.

다음과 같이 이웃을 대표하기로 결정했습니다.

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.