행과 열을 분리해 각각 독립적으로 판단
각 인접 라인 쌍(행 k–k+1 또는 열 k–k+1)에 대해 충돌 가능성 검사
두 라인의 원래 높이 차들을 모두 살펴본다.
그 중 차이가 –1, 0, +1인 경우들은 “한쪽만 올리거나(±1) 또는 둘 다 올리거나(0→0,1→1)” 하면 최종 높이가 같아지는 충돌 후보다.
따라서 해당 라인 쌍에서는
(0→0), (1→1) 조합이 불가(원래 차=0),
(0→1) 조합이 불가(원래 차=+1),
(1→0) 조합이 불가(원래 차=–1)
와 같이 금지된 전이(transition)를 먼저 모아둔다.
만약 이 금지 전이를 제외하고 남은 선택 조합이 하나도 없다면,
“어떻게 해도 이 두 라인 사이에 높이 충돌이 발생” → 해당 방향 전체가 불가능임을 바로 판정한다.
허용 전이 정보를 이용해 DP로 최소 비용 계산
상태: “0부터 i번째 라인까지 고려했고, i번째 라인에서(안 올리기=0/올리기=1) 중 하나를 택했을 때의 최소 누적 비용”
초기:
첫 라인에서는 0을 택하면 비용 0, 1을 택하면 그 라인의 비용 a₁(또는 b₁).
전이:
이렇게 1번부터 n–1번까지 채워 나가면, 마지막 라인에서 안 올리기와 올리기 중 더 적은 비용이 그 방향의 해답이 된다.
행 DP 결과 + 열 DP 결과 = 전체 최소 비용
import sys
input = sys.stdin.readline
INF = 10**30
def solve_one(n, h, cost):
allowed = []
m = len(h)
for k in range(n - 1):
S = {h[k][t] - h[k + 1][t] for t in range(m)}
bad = S & {-1, 0, 1}
ok = {(u, v) for u in (0, 1) for v in (0, 1) if (v - u) not in bad}
if not ok:
return INF
allowed.append(ok)
dp0 = [0, cost[0]]
for i in range(1, n):
dp1 = [INF, INF]
for v in (0, 1):
base = cost[i] * v
best = INF
for u in (0, 1):
if (u, v) in allowed[i - 1] and dp0[u] < best:
best = dp0[u]
dp1[v] = base + best
dp0 = dp1
return min(dp0)
t = int(input())
out = []
for _ in range(t):
n = int(input())
h = [list(map(int, input().split())) for _ in range(n)]
a = list(map(int, input().split()))
b = list(map(int, input().split()))
cost_rows = solve_one(n, h, a)
cost_cols = solve_one(n, list(zip(*h)), b)
ans = cost_rows + cost_cols
out.append(str(ans if ans < INF//2 else -1))
print("\\n".join(out))