Vergi Tarihçisi


9

Giriş

Krallığının vergilerini yönetmekle ilgili bir sıkıntı yaşayan bir vergi tahsildarı var: tarihi kayıtlar büyük bir yangında yanmış.

Mevcut paranın nereden devralındığı açısından kaç olası geçmiş olabileceğini öğrenmek istiyor. Neyse ki, krallığı çok basit.

Krallık, lparayı devralmış birini temsil eden ve olmayan bir kişiyi temsil eden bir 2D boole matrisi ile modellenebilir O. Örneğin:

l O l l

O O O l

l O l O

O O O l

(Her zaman bir dikdörtgen olacaktır)

Gelecek nesilde, krallık daha küçüktür (kurtlar güçlüdür!).

Bir sonraki nesil, bir önceki nesle bindirilmiş gibi görünecektir ( xgelecek nesilde bir soyundan gelen bir yer tutucudur)

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

Bir soyundan çevrelerindeki doğrudan olan atalarının bakacağız (sol üst Yani xgöreceksiniz { l, O, O, O}, bir adlandırılan Hizalanmamış dikdörtgen mahalle )

Eğer sadece bir atadan miras kalmışsa, soyundan onlardan para miras kalır. Birden fazla ata para miras almışsa, kavga eder ve soyundan gelen kişi parayı devralmaz. Hiç kimse para miras almazsa, soyundan gelen kimse parayı devralmaz.

(Birden fazla torun bir atadan miras alabilir)

Yani, yeni nesil şöyle görünecekti:

​
 l l O

 l l O

 l l O
​

Meydan okuma

Giriş

İç dizilerin hepsinin aynı uzunlukta olduğu iki farklı değerin dizilerinden oluşan bir dizi olarak jenerasyonun mevcut durumu.

Örneğin, yukarıdaki örnek için şunlar olabilir:

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

Çıktı

Sonraki neslin girdi olduğu benzersiz önceki nesillerin sayısını temsil eden bir tam sayı.

Cevabın her zaman 2 ^ 30-1'den daha az olacağını varsayabilirsiniz. (Veya 1073741823).

Önceki nesle "preimage" denecek ve bu zorluk ön görüntüleri saymak olacaktır .

puanlama

Bu bir Bu nedenle, her gönderim bilgisayarımda test edilecek ve en az zaman alan gönderim kazanan olacaktır.

Örnek Giriş ve Çıkış

( 1Parayı miras alan bir torun nerede ve 0parayı miras almamış bir torun nerede )

Giriş:

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

Çıktı:

4

Giriş:

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

Çıktı:

254

Giriş:

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

Çıktı:

11567

6
"lOOlLOOOOLLlololoLOLOLOOLOLOLOLL" sayfayı ilk açtığımda gördüm.
Sihirli Ahtapot Urn

Yanıtlar:


4

BuDDy kitaplığını kullanarak C ++

Bu ikili karar diyagramları ile oynamak için güzel bir bahane gibi görünüyordu . Krallık, tatmin edilebileceği yolların sayısını saymamız gereken büyük bir boole formülüne dönüştürülür. Bu (bazen) göründüğünden daha verimli bir şekilde yapılabilir.

Krallık düz bir dizi olarak bir program sabiti ve açıkça verilen boyutlar olarak verilmelidir. (Güzel giriş okuyucu için bir yürütme olarak bırakılır :-)

İşte utanç verici derecede basit kod:

#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) ile derlemek için yükleyin libbdd-devve yapın g++ -std=c++11 -o hist hist.cpp -lbdd. (Gerçekleştirme kütüphanede yapıldığı için seçenekleri optimize etmek neredeyse hiç fark etmez.)

Büyük örnekler çöp toplama ile ilgili mesajlara yol açabilir. Bastırılabilirler, ama onları görmeyi tercih ederim.

bdd_satcountdöndürür double, ancak bu beklenen sonuç aralığı için yeterince iyidir. Aynı sayma tekniği tam (büyük) tamsayılarla mümkündür.

Kod için optimize edilmiştir ROWS<COLS. Sütunlardan çok daha fazla satırınız varsa, matrisi aktarmak iyi bir fikir olabilir.


2.39 saniye. Bu sahip olduğum zamanın yarısı! Bunu kabul edildi olarak işaretleme.
Artyer

1
@Artyer: En uzun süredir saklanan gizli test vakanızı yayınlamak ister misiniz? İsterseniz çözümünüzün yanı sıra.
Andrew Epstein

@AndrewEpstein Geçenlerde bir sabit disk hatası yaşadım ve hem kodu hem de orijinal test senaryolarını kaybettim (Yüzlerce vardı ve en fazla 300 genişliğinde, 10 yüksek olduğunu düşünüyorum). Afedersiniz.
Artyer

3

Python 2.7

Bu sadece naif bir ilk girişim. Özellikle hızlı değil, ama doğru.

İlk gözlem, her hücrenin önceki nesilde tam olarak dört hücreye bağımlı olduğudur. Bu dört hücreyi 4 bitlik bir sayı (0-15) olarak temsil edebiliriz. Kurallara göre, önceki nesilde tam olarak bir komşu hücre ise 1, o zaman mevcut nesildeki belirli bir hücre 1, aksi takdirde olacaktır 0. Bunlar ikisinin güçlerine, yani [1, 2, 4, 8]. Dört ata 4 bitlik bir sayı olarak temsil edildiğinde, başka herhangi bir sayı 0mevcut nesilde bir ile sonuçlanır . Bu bilgilerle, mevcut nesilde bir hücre gördükten sonra, önceki nesildeki mahallenin olasılıklarını sırasıyla dört veya on iki olasılıktan birine daraltabiliriz.

Mahalleyi şu şekilde temsil etmeyi seçtim:

32
10

burada 0 en küçük anlamlı bittir ve bu böyle devam eder.

İkinci gözlem, mevcut nesildeki iki bitişik hücre için, önceki neslin iki mahallesinin çakışmasıdır:

32  32
10  10

veya:

32
10

32
10

Yatay durumda, 2soldaki mahalle 3, sağdaki mahalle ile örtüşmektedir ve benzer şekilde 0soldaki ve sağdaki ile örtüşmektedir 1. Dikey durumda, en 1üstteki mahalle 3, alt mahalle ile 0ve üstte ve 2altta ile benzer şekilde üst üste gelir.

Bu örtüşme, daha önce seçmiş olduğumuz şeylere dayanarak henüz seçilmemiş mahallelerin olanaklarını daraltabileceğimiz anlamına geliyor. Kod, olası ön görüntüleri ilk önce özyinelemeli bir derinlik aramasında soldan sağa, yukarıdan aşağıya doğru çalışır. Aşağıdaki şema, mevcut bir hücrenin olası mahallelerine bakarken hangi önceki mahalleleri dikkate almamız gerektiğini göstermektedir:

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]

İşte kod:

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

Çalıştırmak için:

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
Gizli test senaryolarını yapmak uzun sürüyor, bu yüzden bu sunumu puanlamıyorum. Farklı bir algoritma deneyin, çünkü bu çok yüksek bir zaman karmaşıklığına sahiptir ( O(<something>^n)Sanırım.)
Artyer
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.