USACO Silver T3
'''
找到一种将输入中的数字替换的方法,使得
1. 数字的出现次数符合原加法表
2. 可以通过原加法表交换行,列后得到
然后在所有这种方法之中找到替换后的表格字典序最小的
'''
N = int(input())
li = list()
for i in range(N):
li.append([int(x) for x in input().split()])
'''
对于数字k, 出现 满足i+j = k的索引对的数量 次
k<=N: k-1次
k>N: 2N-k+1次
'''
def sum_table(N):
return [[(i + j) for j in range(1, N+1)] for i in range(1, N+1)]
sumt = sum_table(N)
num_times = {i : i-1 if i<=N else 2*N-i+1 for i in range(2, 2*N+1)}
li_times = {}
for i in li:
for j in i:
if int(j) in li_times.keys():
li_times[int(j)] += 1
else:
li_times[int(j)] = 1
for i in li_times:
li_times[i] = [n for n in num_times if li_times[i] == num_times[n]]
# sort the li_times
li_times = dict(sorted(li_times.items(), key=lambda item: item[0]))
def swap_dict(a, b, d:dict):
d = d.copy()
d[a], d[b] = d[b], d[a]
return d
def swap_list(a, b, l:list):
l = l.copy()
# change a to b, b to a
for i in range(len(l)):
for j in range(len(l[i])):
if l[i][j] == a:
l[i][j] = b
elif l[i][j] == b:
l[i][j] = a
return l
def valid_table(li):
li = li.copy()
'''
行和列的跨度检查:
每行的最大值与最小值差:对于每一行,最大值减最小值必须等于N-1。
每列的最大值与最小值差:同样,每列的最大值减最小值也必须等于N-1。
确定行置换σ和列置换τ:
行置换σ:对于每行i,计算σ(i) = 该行最小值 - 1。所有σ(i)必须构成1到N的排列(无重复且覆盖所有值)。
列置换τ:对于每列j,计算τ(j) = 该列最小值 - 1。所有τ(j)同样必须构成1到N的排列。
元素验证:检查矩阵中的每个元素M[i][j]是否等于σ(i) + τ(j)。若所有元素均满足此条件,则矩阵有效。
'''
# 检查每行的最大值与最小值差是否为 N-1
for row in li:
min_val = min(row)
max_val = max(row)
if max_val - min_val != N - 1:
return False
# 检查每列的最大值与最小值差是否为 N-1
for col in range(N):
column = [li[row][col] for row in range(N)]
min_val = min(column)
max_val = max(column)
if max_val - min_val != N - 1:
return False
# 确定行置换σ
sigma = {}
for i in range(N):
min_val = min(li[i])
sigma[i] = min_val - 1
# 确定列置换τ
tau = {}
for j in range(N):
column = [li[row][j] for row in range(N)]
min_val = min(column)
tau[j] = min_val - 1
# 检查σ和τ是否为1到N的排列
if sorted(sigma.values()) != list(range(1, N+1)) or sorted(tau.values()) != list(range(1, N+1)):
return False
# 验证每个元素是否等于 σ(i) + τ(j)
for i in range(N):
for j in range(N):
if li[i][j] != sigma[i] + tau[j]:
return False
return True
'''
li_times[i]为i替换前可能的原数字列表
3 4 2
5 2 3
6 3 5
original: {2: [2, 6], 3: [3, 5], 4: [4], 5: [3, 5], 6: [2, 6]}
changed : {2: [3, 5], 3: [4], 4: [2, 6], 5: [3, 5], 6: [2, 6]}
每一次交换a, b 会把a, b的列表交换
'''
def find_swaps(swaped, d=li_times):
for i in d:
if i not in d[i]:
flag = False
for num, val in d.items():
if i in val and num not in val:
return find_swaps(swaped + [(i, num)], swap_dict(i, num, d))
return swaped
swap_methods = find_swaps([], li_times)
for a, b in swap_methods:
li = swap_list(a, b, li)
'''
生成所有的 (j, 2N+2-j)的全组合, 存到to_swap[[]]里
'''
to_swap = []
for j in range(2, N + 1):
for k in range(j, N + 1):
'''添加从j到k中的所有数字及其对应'''
to_swap.append([(l, 2*N+2-l) for l in range(j, k + 1)])
smallest = [[float('inf') for _ in range(N)] for _ in range(N)]
def less_than(l1, l2):
for i in range(len(l1)):
for j in range(len(l1[i])):
if l1[i][j] < l2[i][j]:
return True
elif l1[i][j] > l2[i][j]:
return False
return False
for i in to_swap:
lis = li.copy()
for a, b in i:
lis = swap_list(a, b, lis)
if valid_table(lis):
if less_than(lis, smallest):
smallest = [x.copy() for x in lis]
for i in smallest:
print(' '.join([str(x) for x in i]))
评论
其他文章