Nhà sử học thuế


9

Giới thiệu

Có một người thu thuế gặp một số khó khăn trong việc quản lý thuế của vương quốc của mình: các hồ sơ lịch sử đã bị đốt cháy trong một đám cháy lớn.

Anh ta muốn tìm hiểu có bao nhiêu quá khứ có thể có về mặt tiền được thừa kế từ đâu. May mắn thay, vương quốc của anh ấy rất đơn giản.

Vương quốc có thể được mô hình hóa bằng ma trận boolean 2D, nơi lđại diện cho một người được thừa hưởng tiền và Ođại diện cho một người không có. Ví dụ:

l O l l

O O O l

l O l O

O O O l

(Nó sẽ luôn là một hình chữ nhật)

Ở thế hệ tiếp theo, vương quốc nhỏ hơn (Những con sói rất mạnh!).

Thế hệ tiếp theo sẽ trông như thế này, được đặt chồng lên thế hệ trước ( xlà một người giữ chỗ cho một hậu duệ ở thế hệ tiếp theo)

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

Một hậu duệ sẽ xem xét tổ tiên liên quan trực tiếp xung quanh họ (Vì vậy, phía trên bên trái xsẽ thấy { l, O, O, O}, được gọi là một hình chữ nhật hàng xóm Unaligned )

Nếu chỉ có một tổ tiên được thừa hưởng tiền, con cháu sẽ được thừa hưởng tiền từ họ. Nếu có nhiều hơn một tổ tiên đã được thừa kế tiền, họ sẽ chiến đấu và con cháu cuối cùng sẽ không được thừa kế tiền. Nếu không ai được thừa kế tiền, con cháu sẽ không được thừa kế bất kỳ khoản tiền nào.

(Nhiều hơn một hậu duệ có thể thừa hưởng từ một tổ tiên)

Vì vậy, thế hệ tiếp theo sẽ như thế nào:

​
 l l O

 l l O

 l l O
​

Thử thách

Đầu vào

Trạng thái hiện tại của thế hệ, như một mảng các mảng có hai giá trị riêng biệt, trong đó các mảng bên trong đều có cùng độ dài.

Ví dụ, ví dụ trên, nó có thể là:

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

Đầu ra

Một số nguyên biểu thị số lượng thế hệ trước duy nhất trong đó thế hệ tiếp theo là đầu vào.

Bạn có thể giả sử rằng câu trả lời sẽ luôn nhỏ hơn 2 ^ 30 - 1. (hoặc 1073741823).

Thế hệ trước sẽ được gọi là "tiền thân" và thử thách này sẽ là đếm số tiền tố .

Chấm điểm

Đây là một thử thách , vì vậy mỗi bài nộp sẽ được kiểm tra trên máy tính của tôi và bài nộp mất ít thời gian nhất sẽ là người chiến thắng.

Ví dụ đầu vào và đầu ra

(Đâu 1là một hậu duệ được thừa hưởng tiền và 0là một hậu duệ không được thừa kế tiền)

Đầu vào:

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

Đầu ra:

4

Đầu vào:

[[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]]

Đầu ra:

254

Đầu vào:

[[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]]

Đầu ra:

11567

6
"LOOlLOOOOLLlololoLOLOLOOLOLOLOLL" là tất cả những gì tôi thấy khi lần đầu tiên mở trang.
Bạch tuộc ma thuật Urn

Câu trả lời:


4

C ++ sử dụng thư viện BuDDy

Đây dường như là một cái cớ tốt đẹp để chơi với sơ đồ quyết định nhị phân . Vương quốc được chuyển đổi thành một công thức boolean lớn, nơi chúng ta phải đếm số cách mà nó có thể được thỏa mãn. Điều đó có thể (đôi khi) được thực hiện hiệu quả hơn âm thanh.

Vương quốc phải được đưa ra như một hằng số chương trình dưới dạng một mảng phẳng và các kích thước được cung cấp rõ ràng. (Đầu vào đẹp được để lại dưới dạng thực thi cho người đọc :-)

Đây là mã đơn giản đáng xấu hổ:

#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();
}

Để biên dịch với debian 8 (jessie), cài đặt libbdd-devvà làm g++ -std=c++11 -o hist hist.cpp -lbdd. (Tối ưu hóa các tùy chọn làm cho hầu như không có sự khác biệt vì công việc thực sự được thực hiện trong thư viện.)

Các ví dụ lớn có thể dẫn đến các thông điệp về bộ sưu tập rác. Họ có thể bị đàn áp, nhưng tôi thích nhìn thấy họ.

bdd_satcounttrả về một double, nhưng điều đó là đủ tốt cho phạm vi kết quả dự kiến. Kỹ thuật đếm tương tự là có thể với các số nguyên chính xác (lớn).

Mã được tối ưu hóa cho ROWS<COLS. Nếu bạn có nhiều hàng hơn các cột, có thể nên chuyển đổi ma trận.


2,39 giây. Đây là một nửa thời gian mà tôi đã có! Đánh dấu này là chấp nhận.
Artyer

1
@Artyer: Bạn có muốn đăng trường hợp thử nghiệm ẩn lâu nhất của bạn không? Cũng như giải pháp của bạn, nếu bạn muốn.
Andrew Epstein

@AndrewEpstein Gần đây tôi đã gặp một lỗi ổ cứng và mất cả mã và các trường hợp thử nghiệm ban đầu (Có hàng trăm trong số chúng, và chúng có chiều rộng tối đa 300, cao 10 tôi nghĩ). Lấy làm tiếc.
Artyer

3

Python 2.7

Đây chỉ là một nỗ lực đầu tiên ngây thơ. Nó không đặc biệt nhanh, nhưng nó là chính xác.

Quan sát đầu tiên là mỗi tế bào phụ thuộc vào chính xác bốn tế bào ở thế hệ trước. Chúng ta có thể biểu diễn bốn ô đó dưới dạng số 4 bit (0-15). Theo các quy tắc, nếu chính xác một ô lân cận ở thế hệ trước là 1, thì một ô đã cho trong thế hệ hiện tại sẽ là 1, nếu không, nó sẽ như vậy 0. Những người tương ứng với sức mạnh của hai, cụ thể là [1, 2, 4, 8]. Khi bốn tổ tiên được biểu diễn dưới dạng số 4 bit, bất kỳ số nào khác sẽ dẫn đến một 0thế hệ hiện tại. Với thông tin này, khi nhìn thấy một tế bào trong thế hệ hiện tại, chúng ta có thể thu hẹp khả năng của vùng lân cận ở thế hệ trước xuống một trong bốn hoặc một trong mười hai khả năng tương ứng.

Tôi đã chọn đại diện cho khu phố như sau:

32
10

trong đó 0 là bit có ý nghĩa nhỏ nhất, v.v.

Quan sát thứ hai là đối với hai ô liền kề trong thế hệ hiện tại, hai vùng lân cận từ thế hệ trước trùng nhau:

32  32
10  10

hoặc là:

32
10

32
10

Trong trường hợp nằm ngang, 2từ vùng lân cận bên trái trùng với 3vùng lân cận bên phải và tương tự với 0bên trái và 1bên phải. Trong trường hợp dọc, 1từ vùng lân cận trên cùng trùng với 3vùng lân cận phía dưới và tương tự với vùng 0trên cùng và 2phía dưới.

Sự chồng chéo này có nghĩa là chúng ta có thể thu hẹp các khả năng cho các vùng lân cận chưa được phát triển dựa trên những gì chúng ta đã chọn. Mã này hoạt động theo cách từ trái sang phải, từ trên xuống dưới, trong một tìm kiếm theo chiều sâu đệ quy cho các tiền đề có thể. Sơ đồ sau cho biết những vùng lân cận trước mà chúng ta phải xem xét khi xem xét các vùng lân cận có thể có của một ô hiện tại:

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]

Đây là mã:

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]])

Để chạy nó:

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
Việc này mất nhiều thời gian để thực hiện các trường hợp kiểm tra ẩn, vì vậy tôi không chấm điểm bài nộp này. Hãy thử một thuật toán khác, vì thuật toán này có độ phức tạp thời gian quá cao ( O(<something>^n)Tôi nghĩ vậy.)
Artyer
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.