ค้นหาชุดค่าผสมของโพลีโม่ฟรีทั้งหมดภายในพื้นที่เฉพาะด้วยตัวแก้ SAT (Python)


15

ฉันยังใหม่ต่อโลกของนักแก้ปัญหา SAT และต้องการคำแนะนำเกี่ยวกับปัญหาต่อไปนี้

พิจารณาว่า:

❶ฉันมีเซลล์ที่อยู่ติดกันจำนวน 14 เซลล์ในตาราง 4 * 4

❷ฉันมี 5 polyominoes (A, B, C, D, E) ขนาด 4, 2, 5, 2 และ 1

poly โพลีโม่โนเหล่านี้ไม่มีค่าเช่นรูปร่างของพวกมันไม่คงที่และสามารถสร้างรูปแบบที่แตกต่างกันได้

ป้อนคำอธิบายรูปภาพที่นี่

ฉันจะคำนวณการรวมกันที่เป็นไปได้ทั้งหมดของ 5 โพลีโม่ฟรีภายในพื้นที่ที่เลือก (เซลล์สีเทา) ด้วยตัวแก้ SAT ได้อย่างไร

การยืมทั้งสองจากคำตอบที่ลึกซึ้งของ @ spinkus และเอกสาร OR-tools ฉันสามารถสร้างโค้ดตัวอย่างต่อไปนี้ (ทำงานในสมุดบันทึก Jupyter):

from ortools.sat.python import cp_model

import numpy as np
import more_itertools as mit
import matplotlib.pyplot as plt
%matplotlib inline


W, H = 4, 4 #Dimensions of grid
sizes = (4, 2, 5, 2, 1) #Size of each polyomino
labels = np.arange(len(sizes))  #Label of each polyomino

colors = ('#FA5454', '#21D3B6', '#3384FA', '#FFD256', '#62ECFA')
cdict = dict(zip(labels, colors)) #Color dictionary for plotting

inactiveCells = (0, 1) #Indices of disabled cells (in 1D)
activeCells = set(np.arange(W*H)).difference(inactiveCells) #Cells where polyominoes can be fitted
ranges = [(next(g), list(g)[-1]) for g in mit.consecutive_groups(activeCells)] #All intervals in the stack of active cells



def main():
    model = cp_model.CpModel()


    #Create an Int var for each cell of each polyomino constrained to be within Width and Height of grid.
    pminos = [[] for s in sizes]
    for idx, s in enumerate(sizes):
        for i in range(s):
            pminos[idx].append([model.NewIntVar(0, W-1, 'p%i'%idx + 'c%i'%i + 'x'), model.NewIntVar(0, H-1, 'p%i'%idx + 'c%i'%i + 'y')])



    #Define the shapes by constraining the cells relative to each other

    ## 1st polyomino -> tetromino ##
    #                              #      
    #                              # 
    #            #                 # 
    #           ###                # 
    #                              # 
    ################################

    p0 = pminos[0]
    model.Add(p0[1][0] == p0[0][0] + 1) #'x' of 2nd cell == 'x' of 1st cell + 1
    model.Add(p0[2][0] == p0[1][0] + 1) #'x' of 3rd cell == 'x' of 2nd cell + 1
    model.Add(p0[3][0] == p0[0][0] + 1) #'x' of 4th cell == 'x' of 1st cell + 1

    model.Add(p0[1][1] == p0[0][1]) #'y' of 2nd cell = 'y' of 1st cell
    model.Add(p0[2][1] == p0[1][1]) #'y' of 3rd cell = 'y' of 2nd cell
    model.Add(p0[3][1] == p0[1][1] - 1) #'y' of 3rd cell = 'y' of 2nd cell - 1



    ## 2nd polyomino -> domino ##
    #                           #      
    #                           # 
    #           #               # 
    #           #               # 
    #                           # 
    #############################

    p1 = pminos[1]
    model.Add(p1[1][0] == p1[0][0])
    model.Add(p1[1][1] == p1[0][1] + 1)



    ## 3rd polyomino -> pentomino ##
    #                              #      
    #            ##                # 
    #            ##                # 
    #            #                 # 
    #                              #
    ################################

    p2 = pminos[2]
    model.Add(p2[1][0] == p2[0][0] + 1)
    model.Add(p2[2][0] == p2[0][0])
    model.Add(p2[3][0] == p2[0][0] + 1)
    model.Add(p2[4][0] == p2[0][0])

    model.Add(p2[1][1] == p2[0][1])
    model.Add(p2[2][1] == p2[0][1] + 1)
    model.Add(p2[3][1] == p2[0][1] + 1)
    model.Add(p2[4][1] == p2[0][1] + 2)



    ## 4th polyomino -> domino ##
    #                           #      
    #                           # 
    #           #               #   
    #           #               # 
    #                           # 
    #############################

    p3 = pminos[3]
    model.Add(p3[1][0] == p3[0][0])
    model.Add(p3[1][1] == p3[0][1] + 1)



    ## 5th polyomino -> monomino ##
    #                             #      
    #                             # 
    #           #                 # 
    #                             # 
    #                             # 
    ###############################
    #No constraints because 1 cell only



    #No blocks can overlap:
    block_addresses = []
    n = 0
    for p in pminos:
        for c in p:
            n += 1
            block_address = model.NewIntVarFromDomain(cp_model.Domain.FromIntervals(ranges),'%i' % n)
                model.Add(c[0] + c[1] * W == block_address)
                block_addresses.append(block_address)

    model.AddAllDifferent(block_addresses)



    #Solve and print solutions as we find them
    solver = cp_model.CpSolver()

    solution_printer = SolutionPrinter(pminos)
    status = solver.SearchForAllSolutions(model, solution_printer)

    print('Status = %s' % solver.StatusName(status))
    print('Number of solutions found: %i' % solution_printer.count)




class SolutionPrinter(cp_model.CpSolverSolutionCallback):
    ''' Print a solution. '''

    def __init__(self, variables):
        cp_model.CpSolverSolutionCallback.__init__(self)
        self.variables = variables
        self.count = 0

    def on_solution_callback(self):
        self.count += 1


        plt.figure(figsize = (2, 2))
        plt.grid(True)
        plt.axis([0,W,H,0])
        plt.yticks(np.arange(0, H, 1.0))
        plt.xticks(np.arange(0, W, 1.0))


        for i, p in enumerate(self.variables):
            for c in p:
                x = self.Value(c[0])
                y = self.Value(c[1])
                rect = plt.Rectangle((x, y), 1, 1, fc = cdict[i])
                plt.gca().add_patch(rect)

        for i in inactiveCells:
            x = i%W
            y = i//W
            rect = plt.Rectangle((x, y), 1, 1, fc = 'None', hatch = '///')
            plt.gca().add_patch(rect)

ป้อนคำอธิบายรูปภาพที่นี่

ปัญหาคือฉันมีโพลีโม่แบบพิเศษที่มีการกำหนดค่าตายตัว 5 ตัวและฉันไม่รู้ว่าจะกำหนดข้อ จำกัด อย่างไรเนื่องจากแต่ละรูปแบบที่เป็นไปได้สำหรับโพลีโนมิโนนั้นถูกนำมาพิจารณา (หากเป็นไปได้)


ฉันได้ยินเกี่ยวกับเครื่องมือของ Google ครั้งแรก มันเป็นไปได้ที่จะใช้ห้องสมุดหลามมาตรฐานเช่นitertools, numpy, networkx?
mathfux

ฉันต้องการใช้ sat-solver หรือ - tools มากกว่า
ละลาย

@subub มันง่ายที่จะสร้างแบบจำลอง / แก้ปัญหาแบบนี้โดยใช้ภาษา MiniZinc เนื่องจากมีข้อ จำกัด ระดับสูงสำหรับการวางวัตถุที่ผิดปกติบนพื้นผิว ถ้าคุณเดินผ่านหลักสูตรฟรี"การสร้างแบบจำลองขั้นสูงสำหรับการเพิ่มประสิทธิภาพแบบไม่ต่อเนื่อง" บน Courseraคุณจะได้รับการสอนวิธีการทำและให้ตัวอย่างที่เป็นประโยชน์ (และซับซ้อนกว่า) Or-Tools มีอินเทอร์เฟซสำหรับภาษา MiniZinc ดังนั้นคุณยังคงสามารถควบคุมกำลังของมันเพื่อค้นหาวิธีแก้ปัญหาอย่างรวดเร็ว
Patrick Trentin

1
ดูเหมือนว่าน่าสนใจขอบคุณสำหรับตัวชี้ ไม่แน่ใจว่ามันจะตอบปัญหาเฉพาะที่ฉันมี (การกำหนดข้อ จำกัด เกี่ยวกับโพลีโม่แบบอิสระไม่ใช่แบบสแตติก) แต่ฉันจะดูอย่างแน่นอน
ละลาย

1
ฉันต้องขออภัยฉันลืมคำถามนี้ไปจนหมด ได้มีคำถามที่เกี่ยวข้องในแท็กกับคำตอบที่มีรายละเอียดที่ครอบคลุมข้อเสนอแนะก่อนหน้าของฉันเกี่ยวกับการใช้minizinc minizinc
Patrick Trentin

คำตอบ:


10

แก้ไข:ฉันพลาดคำว่า"ฟรี"ในคำตอบดั้งเดิมและให้คำตอบโดยใช้ OR-Tools สำหรับโพลีโม่แบบคงที่ เพิ่มหัวข้อเพื่อให้คำตอบเพื่อรวมโซลูชันสำหรับโพลีโอโนฟรีซึ่ง AFAICT กลายเป็นเรื่องยากที่จะแสดงความแม่นยำในการตั้งโปรแกรม จำกัด ด้วยเครื่องมือ OR

POLYOMINOES คงที่ด้วยหรือเครื่องมือ:

ใช่คุณสามารถทำได้ด้วยโปรแกรม จำกัดใน OR-Tools เครื่องมือ - ไม่รู้อะไรเลยเกี่ยวกับเรขาคณิตกริด 2D ดังนั้นคุณต้องเข้ารหัสเรขาคณิตของแต่ละรูปร่างที่คุณมีในแง่ของข้อ จำกัด ตำแหน่ง เช่นรูปร่างคือชุดของบล็อก / เซลล์ที่ต้องมีความสัมพันธ์บางอย่างกับแต่ละอื่น ๆ จะต้องอยู่ในขอบเขตของตารางและจะต้องไม่ทับซ้อนกัน เมื่อคุณมีโมเดลข้อ จำกัด ของคุณคุณเพียงแค่ขอให้CP-SAT Solverแก้ปัญหาในกรณีของคุณสำหรับวิธีแก้ปัญหาที่เป็นไปได้ทั้งหมด

นี่เป็นข้อพิสูจน์ที่ง่ายมากเกี่ยวกับแนวคิดที่มีรูปทรงสี่เหลี่ยมผืนผ้าสองรูปแบบบนตาราง 4x4 (คุณอาจต้องการเพิ่มรหัสแปลบางส่วนเพื่อไปจากคำอธิบายรูปร่างไปยังชุดของตัวแปร OR-Tools และข้อ จำกัด ในปัญหาขนาดใหญ่ขึ้น เนื่องจากการป้อนข้อ จำกัด ด้วยมือค่อนข้างน่าเบื่อ)

from ortools.sat.python import cp_model

(W, H) = (3, 3) # Width and height of our grid.
(X, Y) = (0, 1) # Convenience constants.


def main():
  model = cp_model.CpModel()
  # Create an Int var for each block of each shape constrained to be within width and height of grid.
  shapes = [
    [
      [ model.NewIntVar(0, W, 's1b1_x'), model.NewIntVar(0, H, 's1b1_y') ],
      [ model.NewIntVar(0, W, 's1b2_x'), model.NewIntVar(0, H, 's1b2_y') ],
      [ model.NewIntVar(0, W, 's1b3_x'), model.NewIntVar(0, H, 's1b3_y') ],
    ],
    [
      [ model.NewIntVar(0, W, 's2b1_x'), model.NewIntVar(0, H, 's2b1_y') ],
      [ model.NewIntVar(0, W, 's2b2_x'), model.NewIntVar(0, H, 's2b2_y') ],
    ]
  ]

  # Define the shapes by constraining the blocks relative to each other.
  # 3x1 rectangle:
  s0 = shapes[0]
  model.Add(s0[0][Y] == s0[1][Y])
  model.Add(s0[0][Y] == s0[2][Y])
  model.Add(s0[0][X] == s0[1][X] - 1)
  model.Add(s0[0][X] == s0[2][X] - 2)
  # 1x2 rectangle:
  s1 = shapes[1]
  model.Add(s1[0][X] == s1[1][X])
  model.Add(s1[0][Y] == s1[1][Y] - 1)

  # No blocks can overlap:
  block_addresses = []
  for i, block in enumerate(blocks(shapes)):
    block_address = model.NewIntVar(0, (W+1)*(H+1), 'b%d' % (i,))
    model.Add(block[X] + (H+1)*block[Y] == block_address)
    block_addresses.append(block_address)
  model.AddAllDifferent(block_addresses)

  # Solve and print solutions as we find them
  solver = cp_model.CpSolver()
  solution_printer = SolutionPrinter(shapes)
  status = solver.SearchForAllSolutions(model, solution_printer)
  print('Status = %s' % solver.StatusName(status))
  print('Number of solutions found: %i' % solution_printer.count)


def blocks(shapes):
  ''' Helper to enumerate all blocks. '''
  for shape in shapes:
    for block in shape:
      yield block


class SolutionPrinter(cp_model.CpSolverSolutionCallback):
    ''' Print a solution. '''

    def __init__(self, variables):
        cp_model.CpSolverSolutionCallback.__init__(self)
        self.variables = variables
        self.count = 0

    def on_solution_callback(self):
      self.count += 1
      solution = [(self.Value(block[X]), self.Value(block[Y])) for shape in self.variables for block in shape]
      print((W+3)*'-')
      for y in range(0, H+1):
        print('|' + ''.join(['#' if (x,y) in solution else ' ' for x in range(0, W+1)]) + '|')
      print((W+3)*'-')


if __name__ == '__main__':
  main()

ให้:

...
------
|    |
| ###|
|  # |
|  # |
------
------
|    |
| ###|
|   #|
|   #|
------
Status = OPTIMAL
Number of solutions found: 60

โพลีโอมิโนฟรี:

ถ้าเราพิจารณาตารางของเซลล์เป็นกราฟปัญหาสามารถตีความเป็นหา K-พาร์ทิชันของเซลล์ของตารางพาร์ทิชันที่แต่ละคนมีขนาดที่เฉพาะเจาะจงและนอกจากพาร์ทิชันแต่ละคนเป็นองค์ประกอบที่เกี่ยวโยงกัน เช่น AFAICT มีความแตกต่างระหว่างองค์ประกอบที่เกี่ยวโยงกันและไม่มีpolyominoและส่วนที่เหลือของคำตอบนี้ทำให้สมมติฐานที่ว่า

การค้นหา "k-partitions ทั้งหมดของเซลล์ของกริดที่แต่ละพาร์ติชันมีขนาดเฉพาะ" เป็นเรื่องเล็กน้อยที่จะแสดงในการเขียนโปรแกรมข้อ จำกัด OR-Tools แต่ส่วนเชื่อมต่อนั้นยาก AFAICT (ฉันพยายามแล้วล้มเหลวสักพัก ... ) ฉันคิดว่าการเขียนโปรแกรมข้อ จำกัด OR-Tools ไม่ใช่วิธีที่ถูกต้อง ฉันสังเกตเห็นว่าการอ้างอิง OR-Tools C ++ สำหรับไลบรารีการเพิ่มประสิทธิภาพเครือข่ายมีบางอย่างเกี่ยวกับส่วนประกอบที่เชื่อมต่อซึ่งอาจจะดูมีค่า แต่ฉันไม่คุ้นเคย ในทางกลับกันโซลูชันการค้นหาแบบเรียกซ้ำที่ไร้เดียงสาใน Python นั้นสามารถทำได้

นี่คือวิธีการแก้ปัญหา "ด้วยมือ" ไร้เดียงสา มันค่อนข้างช้า แต่สามารถใช้กับเคส 4x4 ของคุณได้ ที่อยู่ถูกใช้เพื่อระบุแต่ละเซลล์ในตาราง (โปรดสังเกตว่าการเรียงลำดับหน้า wikiของ alludes กับบางอย่างเช่นอัลกอริทึมนี้เป็นโซลูชันไร้เดียงสาและดูเหมือนว่าจะแนะนำบางอย่างที่มีประสิทธิภาพมากขึ้นสำหรับปัญหาโพลีโนมีนที่คล้ายกัน)

import numpy as np
from copy import copy
from tabulate import tabulate

D = 4 # Dimension of square grid.
KCC = [5,4,2,2] # List of the sizes of the required k connected components (KCCs).
assert(sum(KCC) <= D*D)
VALID_CELLS = range(2,D*D)

def search():
  solutions = set() # Stash of unique solutions.
  for start in VALID_CELLS: # Try starting search from each possible starting point and expand out.
    marked = np.zeros(D*D).tolist()
    _search(start, marked, set(), solutions, 0, 0)
  for solution in solutions:  # Print results.
    print(tabulate(np.array(solution).reshape(D, D)))
  print('Number of solutions found:', len(solutions))

def _search(i, marked, fringe, solutions, curr_count, curr_part):
  ''' Recursively find each possible KCC in the remaining available cells the find the next, until none left '''
  marked[i] = curr_part+1
  curr_count += 1
  if curr_count == KCC[curr_part]: # If marked K cells for the current CC move onto the next one.
    curr_part += 1
    if curr_part == len(KCC): # If marked K cells and there's no more CCs left we have a solution - not necessarily unique.
      solutions.add(tuple(marked))
    else:
      for start in VALID_CELLS:
        if marked[start] == 0:
          _search(start, copy(marked), set(), solutions, 0, curr_part)
  else:
    fringe.update(neighbours(i, D))
    while(len(fringe)):
      j = fringe.pop()
      if marked[j] == 0:
        _search(j, copy(marked), copy(fringe), solutions, curr_count, curr_part)

def neighbours(i, D):
  ''' Find the address of all cells neighbouring the i-th cell in a DxD grid. '''
  row = int(i/D)
  n = []
  n += [i-1] if int((i-1)/D) == row and (i-1) >= 0 else []
  n += [i+1] if int((i+1)/D) == row and (i+1) < D**2 else []
  n += [i-D] if (i-D) >=0 else []
  n += [i+D] if (i+D) < D**2 else []
  return filter(lambda x: x in VALID_CELLS, n)

if __name__ == '__main__':
  search()

ให้:

...
-  -  -  -
0  0  1  1
2  2  1  1
4  2  3  1
4  2  3  0
-  -  -  -
-  -  -  -
0  0  4  3
1  1  4  3
1  2  2  2
1  1  0  2
-  -  -  -
Number of solutions found: 3884

สิ่งนี้มีประโยชน์มากขอบคุณมาก สิ่งหนึ่งที่เป็นปัญหาคือตัวอย่างของคุณใช้ได้กับโพลีโอโมของรูปร่างคงที่เท่านั้นคำถามนี้เกี่ยวกับโพลีโพโมฟรี (จำนวนเซลล์คงที่ แต่มีรูปร่างแตกต่างกันคำถามจะถูกแก้ไขเพื่อความชัดเจน) ตามตัวอย่างของคุณเราจะต้องเขียนโค้ดอย่างหนักทุกรูปร่างที่เป็นไปได้ (+ การหมุน + การสะท้อนแสง) สำหรับโพลีโนมิโนแต่ละอันที่มีขนาด S ... ซึ่งไม่สามารถใช้งานได้ คำถามยังคงเป็นไปได้หรือไม่ที่จะใช้ข้อ จำกัด ดังกล่าวกับเครื่องมือ OR?
solub

โอ้ไม่ได้รับส่วน "ฟรี" อืมปัญหาสามารถใส่ได้ "find 5-partition ของ 25-omino ที่ 25-omino ถูก จำกัด กับตาราง WxH และแต่ละพาร์ติชัน 5 ยังเป็น X-omino สำหรับ X = (7,6,6 , 4,2) .. " ฉันเดาว่าเป็นไปได้ที่จะทำใน OR-Tools แต่มีกลิ่นเหมือนว่ามันจะง่ายกว่าที่จะนำ CSP กลับมาติดตามความลึกก่อนค้นหาสิ่งนี้โดยตรง: ค้นหา 25-ominos ที่เป็นไปได้ สำหรับแต่ละ 25-omino ที่เป็นไปได้ทำการค้นหา backtracking CSP โดยเลือก X building X-omino ภายใน 25 โดมิโนจนกระทั่งคุณพบโซลูชันที่สมบูรณ์หรือต้องย้อนกลับ
spinkus

เพิ่มบางสิ่งเช่นโซลูชันการค้นหาตรงแบบไร้เดียงสาที่ฉันพูดถึงในความคิดเห็นก่อนหน้าเพื่อความสมบูรณ์
spinkus

5

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

รหัสนี้ค้นหาโซลูชัน 3884 ทั้งหมด:

from ortools.sat.python import cp_model

cells = {(x, y) for x in range(4) for y in range(4) if x > 1 or y > 0}
sizes = [4, 2, 5, 2, 1]
num_polyominos = len(sizes)
model = cp_model.CpModel()

# Each cell is a member of one polyomino
member = {
    (cell, p): model.NewBoolVar(f"member{cell, p}")
    for cell in cells
    for p in range(num_polyominos)
}
for cell in cells:
    model.Add(sum(member[cell, p] for p in range(num_polyominos)) == 1)

# Each polyomino contains the given number of cells
for p, size in enumerate(sizes):
    model.Add(sum(member[cell, p] for cell in cells) == size)

# Find the border of each polyomino
vertices = {
    v: i
    for i, v in enumerate(
        {(x + i, y + j) for x, y in cells for i in [0, 1] for j in [0, 1]}
    )
}
edges = [
    edge
    for x, y in cells
    for edge in [
        ((x, y), (x + 1, y)),
        ((x + 1, y), (x + 1, y + 1)),
        ((x + 1, y + 1), (x, y + 1)),
        ((x, y + 1), (x, y)),
    ]
]
border = {
    (edge, p): model.NewBoolVar(f"border{edge, p}")
    for edge in edges
    for p in range(num_polyominos)
}
for (((x0, y0), (x1, y1)), p), border_var in border.items():
    left_cell = ((x0 + x1 + y0 - y1) // 2, (y0 + y1 - x0 + x1) // 2)
    right_cell = ((x0 + x1 - y0 + y1) // 2, (y0 + y1 + x0 - x1) // 2)
    left_var = member[left_cell, p]
    model.AddBoolOr([border_var.Not(), left_var])
    if (right_cell, p) in member:
        right_var = member[right_cell, p]
        model.AddBoolOr([border_var.Not(), right_var.Not()])
        model.AddBoolOr([border_var, left_var.Not(), right_var])
    else:
        model.AddBoolOr([border_var, left_var.Not()])

# Each border is a circuit
for p in range(num_polyominos):
    model.AddCircuit(
        [(vertices[v0], vertices[v1], border[(v0, v1), p]) for v0, v1 in edges]
        + [(i, i, model.NewBoolVar(f"vertex_loop{v, p}")) for v, i in vertices.items()]
    )

# Print all solutions
x_range = range(min(x for x, y in cells), max(x for x, y in cells) + 1)
y_range = range(min(y for x, y in cells), max(y for x, y in cells) + 1)
solutions = 0


class SolutionPrinter(cp_model.CpSolverSolutionCallback):
    def OnSolutionCallback(self):
        global solutions
        solutions += 1
        for y in y_range:
            print(
                *(
                    next(
                        p
                        for p in range(num_polyominos)
                        if self.Value(member[(x, y), p])
                    )
                    if (x, y) in cells
                    else "-"
                    for x in x_range
                )
            )
        print()


solver = cp_model.CpSolver()
solver.SearchForAllSolutions(model, SolutionPrinter())
print("Number of solutions found:", solutions)

4

สำหรับแต่ละโพลีโอโนมิโนและแต่ละเซลล์บนซ้ายที่เป็นไปได้คุณมีตัวแปรบูลีนที่ระบุว่าเซลล์นี้เป็นส่วนบนซ้ายของสี่เหลี่ยมผืนผ้าล้อมรอบหรือไม่

สำหรับแต่ละเซลล์และแต่ละโพลีโนมีคุณมีตัวแปรบูลีนที่ระบุว่าเซลล์นี้ถูกครอบครองโดยโพลีโน่นี้

ทีนี้สำหรับแต่ละเซลล์และแต่ละโพลีโนมีคุณมีชุดของความหมาย: เลือกเซลล์ด้านบนซ้ายหมายถึงแต่ละเซลล์ถูกครอบครองโดยโพลีโน่นี้จริง ๆ

จากนั้นข้อ จำกัด : สำหรับแต่ละเซลล์อย่างมากที่สุดหนึ่งโพลีโนมีอยู่สำหรับแต่ละโพลีโนมีเซลล์เดียวที่อยู่ด้านบนซ้ายของมัน

นี่เป็นปัญหาบูลีนที่แท้จริง


ขอบคุณมากสำหรับคำตอบ! ฉันไม่รู้จะนำเครื่องมือนี้ไปใช้กับเครื่องมือได้หรือไม่มีตัวอย่างใดบ้าง (จากตัวอย่างของไพ ธ อนที่มีให้) ที่คุณจะแนะนำเป็นพิเศษเพื่อช่วยฉันเริ่มต้นใช้งานหรือไม่
solub

ฉันเสียใจอย่างแท้จริงเพราะฉันไม่เข้าใจคำตอบของคุณ ไม่แน่ใจว่าคำว่า "การปิดล้อมสี่เหลี่ยมผืนผ้า" หมายถึงอะไรหรืออย่างไร "สำหรับแต่ละเซลล์และแต่ละโพลีโนมิโน" จะถูกแปลเป็นรหัส อย่างไรก็ตามคุณจะช่วยบอกฉันได้ไหมถ้าคำอธิบายของคุณตอบโจทย์กรณีของโพลีเมียร์ฟรี (คำถามถูกแก้ไขเพื่อความชัดเจน)
solub
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.