Say we determine a conflict along some column so that M(r1, c) is equal to M(r2, c). The change in one of the cells has the potential to affect its row (r1 or r2), but since the allowed range is up to 2n, we are always guaranteed to have an available value, even if all others in both the associated row and column are already distinct. This means we can make independent choices about which values to change.
Also consider the worst case, where we first change all n-1
values in one row to match all n-1
values in the column of the n
th value that we are about to change, preventing us from using those values as candidates for the n
th. Even then, we’ve only used n
values, but we’re allowed 2n
.
What we still need to consider is how many conflicting pairs can one change address, which is illustrated in the example in the question description,
11
21
where the choice of which 1 to change has an effect on the answer. A cell can have an effect on at most 2 conflicting pairs, one for each axis.
We can select cells to change by the priority of how many conflicting pairs they address.
Python code with brute force comparison:
import collections
import random
def get_next(i, j, n):
if j == n - 1:
return i + 1, 0
return i, j + 1
def is_valid(M, i, j, v):
for ii in range(i):
if M[ii][j] == v:
return False
for jj in range(j):
if M[i][jj] == v:
return False
return True
def brute_force(M):
n = len(M)
def f(i, j, count):
if i == n:
return count
curr = M[i][j]
best = n**2 - 1
for v in range(1, 2*n + 1):
if is_valid(M, i, j, v):
M[i][j] = v
ii, jj = get_next(i, j, n)
new_count = count + int(
curr != v)
best = min(
best,
f(ii, jj, new_count))
M[i][j] = curr
return best
return f(0, 0, 0)
def get_counts(counts_list):
return sum([
sum([max(0, c - 1) for c in counts.values()])
for counts in counts_list])
def f(M):
n = len(M)
row_counts = [collections.defaultdict(int) for _ in range(n)]
col_counts = [collections.defaultdict(int) for _ in range(n)]
for i in range(n):
for j in range(n):
row_counts[i][M[i][j]] += 1
col_counts[j][M[i][j]] += 1
change_count = 0
for i in range(n):
for j in range(n):
v = M[i][j]
if row_counts[i][v] > 1 and col_counts[j][v] > 1:
row_counts[i][v] -= 1
col_counts[j][v] -= 1
change_count += 1
return (change_count
+ get_counts(row_counts)
+ get_counts(col_counts))
num_tests = 1
min_n = 1
max_n = 3
for _ in range(num_tests):
n = random.randint(min_n, max_n)
M = [[random.randint(1, 2*n) for j in range(n)]
for i in range(n)]
print(M)
print((brute_force(M), f(M)))
n
?1 <= n <= 300
.i
and columnj
, collecting numbers from these2n-1
cells, we should end up with distinct numbers? I’ve included the only example input I have for clarification, and it seems that this is not the case. Am I missing something? I would greatly appreciate it if you could provide additional details.