นักประวัติศาสตร์ภาษี


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จะเห็น { l, O, O, O} เรียกว่าเขตสี่เหลี่ยม unaligned )

หากบรรพบุรุษเพียงคนเดียวเท่านั้นที่ได้รับเงินมรดกผู้สืบทอดจะได้รับเงินจากพวกเขา หากมีบรรพบุรุษมากกว่าหนึ่งคนได้รับมรดกพวกเขาจะทะเลาะกันและลูกหลานจะจบลงด้วยการไม่รับมรดก หากไม่มีใครได้รับมรดกผู้สืบทอดจะไม่ได้รับเงินใด ๆ

(ผู้สืบทอดมากกว่าหนึ่งคนสามารถสืบทอดจากบรรพบุรุษหนึ่งคน)

ดังนั้นรุ่นต่อไปจะมีลักษณะเช่น:

​
 l l O

 l l O

 l l O
​

ท้าทาย

อินพุต

สถานะปัจจุบันของการสร้างเป็นอาร์เรย์ของค่าสองค่าที่ต่างกันโดยที่อาร์เรย์ภายในทั้งหมดมีความยาวเท่ากัน

เช่นสำหรับตัวอย่างข้างต้นอาจเป็น:

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

เอาท์พุต

จำนวนเต็มแทนจำนวนรุ่นก่อนหน้าที่ไม่ซ้ำกันโดยที่รุ่นถัดไปคืออินพุต

คุณสามารถสันนิษฐานได้ว่าคำตอบจะน้อยกว่า 2 ^ 30 - 1 เสมอ (หรือ 1073741823)

รุ่นก่อนหน้านี้จะถูกเรียกว่า "preimage" และความท้าทายนี้จะนับ preimages

เกณฑ์การให้คะแนน

มันคือ ความท้าทายดังนั้นการส่งแต่ละครั้งจะถูกทดสอบบนคอมพิวเตอร์ของฉันและการส่งที่ใช้เวลาน้อยที่สุดจะเป็นผู้ชนะ

ตัวอย่างอินพุตและเอาต์พุต

( 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
"lOOlLOOOOLLlololoLOLOLOOLOLOLOLOLL" เป็นสิ่งที่ฉันเห็นเมื่อฉันเปิดหน้าแรก
Magic Octopus Urn

คำตอบ:


4

C ++ ใช้ไลบรารี BuDDy

นี้ดูเหมือนจะเป็นข้อแก้ตัวที่ดีที่จะเล่นกับแผนภาพการตัดสินใจแบบไบนารี ราชอาณาจักรได้รับการดัดแปลงเป็นสูตรบูลีนขนาดใหญ่ที่เราต้องนับจำนวนวิธีที่จะทำให้พอใจ ที่สามารถทำได้บางครั้งมีประสิทธิภาพมากกว่าที่มันฟัง

ราชอาณาจักรจะต้องได้รับเป็นค่าคงที่โปรแกรมเป็นอาเรย์แบนและมิติที่กำหนดอย่างชัดเจน (อินพุตที่ดีถูกปล่อยให้เป็นตัวประมวลผลสำหรับผู้อ่าน :-)

นี่คือรหัสง่าย ๆ ที่น่าอาย:

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

เพื่อรวบรวมกับเดเบียน 8 (เจสซี่), ติดตั้งและทำlibbdd-dev g++ -std=c++11 -o hist hist.cpp -lbdd(ตัวเลือกการปรับให้เหมาะสมทำให้แทบไม่แตกต่างกันเพราะทำงานจริงในห้องสมุด)

ตัวอย่างขนาดใหญ่สามารถนำไปสู่ข้อความเกี่ยวกับการรวบรวมขยะ พวกเขาอาจถูกระงับ แต่ฉันชอบที่จะเห็นพวกเขา

bdd_satcountส่งคืน a doubleแต่นั่นดีพอสำหรับช่วงผลลัพธ์ที่คาดหวัง เทคนิคการนับแบบเดียวกันนั้นเป็นไปได้ด้วยจำนวนเต็ม (ใหญ่) ที่แน่นอน

ROWS<COLSรหัสที่เหมาะสำหรับ หากคุณมีแถวมากกว่าคอลัมน์จำนวนมากมันอาจเป็นความคิดที่ดีที่จะย้ายเมทริกซ์


2.39 วินาที นี่เป็นเวลาครึ่งหนึ่งที่ฉันมี! ทำเครื่องหมายว่ายอมรับแล้ว
2560

1
@Artyer: คุณสนใจที่จะโพสต์กรณีทดสอบที่ซ่อนอยู่ยาวที่สุดของคุณหรือไม่ เช่นเดียวกับวิธีแก้ปัญหาของคุณหากคุณต้องการ
Andrew Epstein

@AndrewEpstein ฉันเพิ่งมีความล้มเหลวของฮาร์ดไดรฟ์และสูญเสียทั้งรหัสและกรณีการทดสอบเดิม (มีหลายร้อยรายการและพวกเขามีความกว้างสูงสุด 300 กว้าง 10 สูงฉันคิด) ขอโทษ
Artyer

3

Python 2.7

นี่เป็นเพียงความพยายามครั้งแรก มันไม่เร็วโดยเฉพาะ แต่ถูกต้อง

การสังเกตครั้งแรกคือว่าแต่ละเซลล์ขึ้นอยู่กับสี่เซลล์ในรุ่นก่อนหน้า เราสามารถแทนเซลล์ทั้งสี่เหล่านั้นเป็นตัวเลข 4 บิต (0-15) ตามกฎถ้าว่าเซลล์เพื่อนบ้านหนึ่งในรุ่นก่อนหน้านี้1แล้วเซลล์ได้รับในรุ่นปัจจุบันจะมีมิฉะนั้นก็จะเป็น1 0สิ่งเหล่านั้นสอดคล้องกับพลังของสองคือ[1, 2, 4, 8]. เมื่อบรรพบุรุษทั้งสี่แสดงเป็นหมายเลข 4 บิตหมายเลขอื่นใดจะส่งผลให้ a 0ในรุ่นปัจจุบัน ด้วยข้อมูลนี้เมื่อเห็นเซลล์ในยุคปัจจุบันเราสามารถ จำกัด ความเป็นไปได้ของย่านในรุ่นก่อนหน้าเป็นหนึ่งในสี่หรือหนึ่งในสิบสองความเป็นไปได้ตามลำดับ

ฉันเลือกที่จะเป็นตัวแทนของพื้นที่ใกล้เคียงดังนี้:

32
10

โดยที่ 0 คือบิตที่มีนัยสำคัญน้อยที่สุดและอื่น ๆ

การสังเกตครั้งที่สองนั้นสำหรับสองเซลล์ที่อยู่ติดกันในการสร้างปัจจุบันทั้งสองละแวกใกล้เคียงจากรุ่นที่ทับซ้อนกันก่อนหน้า:

32  32
10  10

หรือ:

32
10

32
10

ในกรณีแนวนอนนั้น2จากบริเวณใกล้เคียงทางซ้ายจะทับซ้อนกับ3จากย่านที่เหมาะสมและในทำนองเดียวกันกับ0ด้านซ้ายและ1ด้านขวา ในกรณีแนวตั้งนั้น1จากบริเวณใกล้เคียงด้านบนจะทับซ้อนกับ3จากพื้นที่ใกล้เคียงด้านล่างและในทำนองเดียวกันกับที่0อยู่ด้านบนและ2ด้านล่าง

การทับซ้อนกันนี้หมายความว่าเราสามารถ จำกัด ความเป็นไปได้สำหรับพื้นที่ใกล้เคียงที่ยังไม่ถูกเลือกตามสิ่งที่เราได้เลือกไว้แล้ว รหัสทำงานได้จากซ้ายไปขวาบนลงล่างในการค้นหาเชิงลึกแบบวนซ้ำเพื่อค้นหา preimages ที่เป็นไปได้ แผนภาพต่อไปนี้บ่งบอกถึงย่านที่คุ้นเคยก่อนหน้านี้ที่เราต้องพิจารณาเมื่อมองดูย่านที่เป็นไปได้ของเซลล์ปัจจุบัน:

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.