很好的一道搜索题,题目的要求很简单(至少听上去是这样的),用火柴棒拼成一个的正方形(),事先拆掉了一些火柴棒,那么至少还需要去掉多少根火柴棒才能使拼出的图形中没有正方形(摧毁所有正方形)?
数据不算大,显然使用dfs。倘若讨论每根火柴是否已被拿走,没被拿走就拿走,同时对当前状态的正方形个数进行计数,与之前的进行比较从而达到剪枝的目的,这样做,哪怕后面使用位运算存储累计拿走火柴棒的状态,仍然会超时(也有可能超内存)。
于是,我们改变思路,我们还是需要看火柴是否被拿走,但我们从正方形的角度进行考虑,这样计数时的复杂度就会由降低到,然而仍然会超时。
注意到,尽管我们会将此时拿走火柴棒的次数与全局变量(最后输出的结果)进行比较,但很多时候当前状态已经显然会超出,因此我们使用算法(我也是现学的,感觉挺好理解,不明白的可以看这个:IDA* - OI Wiki (oi-wiki.org))。
使用该算法时,我们首先定义一个估价函数,用于进行判断当前状态还需要的最少火柴棒数目(准确地说,可能会比实际达到的数目还要少,但剪枝的效率极高)。
再之后,我们只需要进行正常的dfs了,同时添加一些剪枝,比如一个正方形删掉任意边都搜不出来,那么整个状态就可以扔掉;比如我们定义一个布尔变量,一旦找到,那么就结束搜索;比如我们使用循环,并定义一个变量,最初存储估价函数的值,之后搜索,搜不到就加一,搜到了就可以输出并结束进程了。
说了这么多可能也没有说明白,看看代码叭~
import copy
import sys
sys.setrecursionlimit(1 << 30)
found = False
def check1(x, tmp):
for y in graph[x]:
if tmp[y]:
return False
return True
def check2(x):
for y in graph[x]:
if judge[y]:
return False
return True
def estimate():
cnt = 0
tmp = copy.deepcopy(judge)
for x in range(1, total+1):
if check1(x, tmp):
cnt += 1
for u in graph[x]:
tmp[u] = True
return cnt
def dfs(t):
global found
if t + estimate() > limit:
return
for x in range(1, total+1):
if check2(x):
for y in graph[x]:
judge[y] = True
dfs(t+1)
judge[y] = False
if found:
return
return
found = True
for _ in range(int(input())):
n = int(input())
lst = list(map(int, input().split()))
d, m, nums, total = 2*n+1, lst[0], lst[1:], 0
graph = {}
for i in range(n):
for j in range(n):
for k in range(1, n+1):
if i+k <= n and j+k <= n:
total += 1
graph[total] = []
for p in range(1, k+1):
graph[total] += [d*i+j+p, d*(i+p)+j-n, d*(i+p)+j-n+k, d*(i+k)+j+p]
judge = [False for _ in range(2*n*(n+1)+1)]
for num in nums:
judge[num] = True
limit = estimate()
found = False
while True:
dfs(0)
if found:
print(limit)
break
limit += 1