Q.60:セルの結合パターン

カテゴリ:プログラミング(Python)、アルゴリズム、Atcorder


➊ 問題概要

その昔『プログラマ脳を鍛える数学パズル』を読んで解説が難解だったため、わかりやすくまとめてみました

<セルの結合パターン>

2行2列のセルがあったとき,これを結合可能なパターンは8通りある

正方形または長方形以外の形が生じた場合はNGとなる


設問①: 3行3列の場合に結合してできるパターンは何通りか?

例) 2行2列の場合のOKパターンとNGパターン



設問②: 3行3列の場合に1×1のセルが無いように結合するパターンは何通りか?

例) 2行2列の場合のNGパターン




➋ 赤線と青線でパターンを洗い出す

N=3の時

赤線が6本、青線が6本存在するものとする ※ 赤線をwidth、青線をlengthとする
それぞれ線の表示・非表示を2進数で表現させる(表示:1、非表示:0)

widthとlengthに図に表すと以下となる

widthとlengthの表示・非表示でパターンを全て表すことができる

今までの解説をコードにすると以下になる、パターンは4096通りあることが分かる

import itertools

N = 3
CNT = 0
for wed in itertools.product([0,1],repeat=N*(N-1)):
    for len in itertools.product([0,1],repeat=N*(N-1)):
            print([wed],[len])
            CNT += 1

print(CNT)

例えば width , length = (0, 0, 0, 0, 0, 0) , (0, 0, 0, 0, 0, 0)のときは
格子点からの線が全て非表示のため、大きい1つの正方形となる


❸ 格子点を設定し、十字でwidth1,2とlength1,2を考える

次は格子点(lattice)に注目してlat1とlat2を使いつつ、width1と2、length1と2を求める
3×3の場合だと格子点は4つ存在する

N=3、width =(1, 1, 0, 0, 1, 0) のとき width1 = width[lat1*(N-1)+lat2] width2 = width[(lat1+1)*(N-1)+lat2]

N=3、length =(1, 1, 0, 0, 1, 0) のとき length1 = length[lat1 * N + lat2] length2 = length[lat1 * N + lat2 + 1]

表を見る通り、それぞれの格子点で左右上下で線の表示・非表示を確認している


❹ ここまでのおさらい

ここまでで以下を実施している

・正方形のパターンを赤線と青線に注目して洗い出す(表示:1、非表示:0)

・格子点を設定する

・格子点ごとに十字でwidth1,2とlength1,2を考える


今までの解説をコードにすると

import itertools
 
N = 3,CNT = 0
 
def check1(width, length):

    for lat1 in range(N - 1):
        for lat2 in range(N - 1):
            width1 = width[lat1 * (N - 1) + lat2]
            width2 = width[(lat1 + 1) * (N - 1) + lat2]
            length1 = length[lat1 * N + lat2]
            length2 = length[lat1 * N + lat2 + 1]
 
    #print(width,length)
    return True

for wed in itertools.product([0, 1], repeat=N * (N - 1)):
   for len in itertools.product([0, 1], repeat=N * (N - 1)):
      if check1(wed, len):
          CNT += 1

print(CNT)

❺ Badパターンに合致する形を除外する

【設問①を求める考え方】

全てのパターン(4096通り)を洗い出して,Badパターンは除いてカウントすれば良い
ここで重要なのはBadパターン条件を整理することである

★ Badパターン(2つ結合する場合)

★ Badパターン(1つ結合する場合)

よって今までの要素をコードにすると設問① cnt1 = 322が求められる

import itertools
N = 3

def solve():  
    cnt1 = cnt2 = 0
    for wed in itertools.product([0, 1], repeat=N * (N - 1)):
        for len in itertools.product([0, 1], repeat=N * (N - 1)):
            if check1(wed, len):
                cnt1 += 1

    print("cnt1 =", cnt1)

def check1(width, length):
    #結合パターン
    for lat1 in range(N - 1):
        for lat2 in range(N - 1):
            width1 = width[lat1 * (N - 1) + lat2]
            width2 = width[(lat1 + 1) * (N - 1) + lat2]
            length1 = length[lat1 * N + lat2]
            length2 = length[lat1 * N + lat2 + 1]
            # 格子点で2箇所結合するNGパターン
            bad2_1 = width1 and not width2 and length1 and not length2
            bad2_2 = not width1 and width2 and length1 and not length2
            bad2_3 = width1 and not width2 and not length1 and length2
            bad2_4 = not width1 and width2 and not length1 and length2
            nogood1 = bad2_1 or bad2_2 or bad2_3 or bad2_4

            # 格子点で1所結合するNGパターン
            bad1_1 = width1 and not width2 and not length1 and not length2
            bad1_2 = not width1 and width2 and not length1 and not length2
            bad1_3 = not width1 and not width2 and length1 and not length2
            bad1_4 = not width1 and not width2 and not length1 and length2
            nogood2 = bad1_1 or bad1_2 or bad1_3 or bad1_4
            
            if nogood1 or nogood2:
                return False
    #print(width,length)
    return True

if __name__ == '__main__':
    solve()

設問①でだいぶ長くなってしまったので設問②は次回以降とします、お楽しみに~ ☺