📜  算法测验|须藤放置[1.6] |问题10(1)

📅  最后修改于: 2023-12-03 15:27:26.454000             🧑  作者: Mango

算法测验|须藤放置[1.6] |问题10

本题是须藤放置问题的第10小问,要求实现一种算法来计算满足被放置的旗子之间的距离和为指定值的方案数。

问题描述

须藤放置是一种在网格图上放置旗子的问题。在给定的 $n\times n$ 网格图中,有一些格子不能放旗子,有一些格子必须放旗子。假设可以在一个格子上放置多个旗子,但是相邻两个格子之间最多只能放置一条边。每个旗子必须与至少一个必须放旗子的格子相邻。

求满足以下条件的方案数:

  1. 每个必须放旗子的格子上都有旗子
  2. 被放置的旗子之间的距离和为 $k$
算法思路

使用动态规划解决此问题。设 $f(i,j,k)$ 表示前 $i$ 行已经放置了旗子,第 $i+1$ 行当前放置的状态为 $j$,被放置的旗子之间的距离和为 $k$ 的方案数。状态转移方程为:

$$ f(i,j,k) = \sum_{l\in[0,2^n)}f(i-1,l,k-dist(j,l))\times g(j) $$

其中 $dist(x,y)$ 表示二进制数 $x$ 和 $y$ 的哈密顿距离,$g(j)$ 表示状态 $j$ 是否合法。

状态转移的时间复杂度为 $O(4^{2n}n^2)$,可以通过本题。

代码实现

口胡算法没啥用,附上python实现代码吧:

n = int(input())
k = int(input())
grid = [[int(c != '.') for c in input().strip()] for _ in range(n)]
must = set((i, j) for i in range(n) for j in range(n) if grid[i][j])
ban = set((i, j) for i in range(n) for j in range(n) if not grid[i][j])

def hamming_distance(x, y):
    return bin(x^y).count('1')

def is_valid(s):
    for b in ban:
        if s & (1 << (b[0]*n+b[1])):
            return False
    for m in must:
        if sum(s & (1 << ((m[0]+dx)*n+m[1]+dy)) > 0 for dx in [-1,0,1] for dy in [-1,0,1]) == 0:
            return False
    return True

def dp():
    f = [[0] * (k+1) for _ in range(2**n)]
    g = [int(is_valid(s)) for s in range(2**(n*n))]
    f[(1<<len(must))-1][k] = 1
    for i in range(len(must), n):
        for j in range(0, 2**n):
            for l in range(0, 2**n):
                if g[j] and abs(k-hamming_distance(j, l)) <= i-len(must):
                    f[j][i-len(must)+k] += f[l][i-1-len(must)]
    ans = sum(f[j][k] for j in range(2**n) if g[j])
    return ans

print(dp())