方法思路
对角线定义:右对角线由i+j标识,左对角线由j-i标识。
统计方法:遍历矩阵中的每个元素,使用两个字典分别统计右对角线和左对角线上的元素出现次数。
累加对数:对于每个元素,累加当前对角线中已存在的相同值的数量,然后更新计数器。
解决代码:
from collections import defaultdict,Counter
n,m = map(int , input().split())
g = [ list(map(int,input().split())) for _ in range(n)]
ans = 0
left = defaultdict(Counter)
right = defaultdict(Counter)
for i in range(n) :
for j in range(m):
x = g[i][j]
ans += left[j-i][x]+right[i+j][x]
left[j-i][x]+=1
right[i+j][x]+=1
print(ans*2)
我们发现有新朋友defaultdict,Counter,先来看看left和right变成了什么
from collections import defaultdict,Counter
n,m = map(int , input().split())
g = [ list(map(int,input().split())) for _ in range(n)]
ans = 0
left = defaultdict(Counter)
right = defaultdict(Counter)
for i in range(n) :
for j in range(m):
x = g[i][j]
ans += left[j-i][x]+right[i+j][x]
left[j-i][x]+=1
right[i+j][x]+=1
print(ans*2)
print('left:',left)
print('right:',right)
# 测试用例:
# 3 2
# 1 2
# 2 3
# 3 2
# 6
# left: defaultdict(<class 'collections.Counter'>, {0: Counter({1: 1, 3: 1}), 1: Counter({2: 1}), -1: Counter({2: 2}), -2: Counter({3: 1})})
# right: defaultdict(<class 'collections.Counter'>, {0: Counter({1: 1}), 1: Counter({2: 2}), 2: Counter({3: 2}), 3: Counter({2: 1})})
原理与作用详解:defaultdict(Counter)
的本质
1. 先理解普通字典的局限性
普通字典(dict
)在访问不存在的键时会直接报错 KeyError
。
2. defaultdict
的作用
defaultdict
是 collections
模块中的一个字典变种。它的核心功能是:
自动为不存在的键生成默认值。
默认值的类型由你指定(例如 int
、list
、Counter
等)。
from collections import defaultdict
d = defaultdict(int) # 指定默认值为整数0
d["apple"] += 1 # 自动创建键"apple",初始值0,然后+1 → 最终d["apple"] = 1
3. Counter
的作用
Counter
是 collections
模块中的另一个工具,专门用于快速统计元素出现次数。
from collections import Counter
lst = ["apple", "banana", "apple", "orange"]
counter = Counter(lst)
print(counter) # 输出:{"apple":2, "banana":1, "orange":1}
4. defaultdict(Counter)
的联合使用
当我们将 defaultdict
的默认值类型指定为 Counter
时,效果如下:
每个键(例如对角线标识 i+j
)对应的值是一个独立的 Counter
。
每个 Counter
可以自动统计该键下的子键(例如矩阵中的数值 x
)的出现次数。
from collections import defaultdict, Counter
# 创建一个字典,每个键的默认值是一个空的Counter
dd = defaultdict(Counter)
# 操作示例
dd["diag1"]["apple"] += 1 # 自动创建键"diag1",值是一个Counter,统计"apple"出现1次
dd["diag1"]["apple"] += 1 # 值变为{"apple":2}
dd["diag2"]["banana"] = 5 # 自动创建键"diag2",值是一个Counter,统计"banana"出现5次
5. 在题目中的具体应用
题目中需要统计每个对角线上的数值出现次数:
右对角线:用 i+j
作为键,对应一个 Counter
,记录该对角线上每个数值的出现次数。
左对角线:用 j-i
作为键,对应另一个 Counter
。
for i in range(n):
for j in range(m):
x = g[i][j]
# 查询当前对角线是否已有x的计数
ans += right[i+j][x] # 右对角线上之前x出现的次数
ans += left[j-i][x] # 左对角线上之前x出现的次数
# 更新计数器
right[i+j][x] += 1
left[j-i][x] += 1
6. 为什么不用 defaultdict(int)
?
假设用 defaultdict(int)
:
right = defaultdict(int)
# 当处理右对角线时:
key = i + j
ans += right[(key, x)] # 需要将对角线和x组合成一个复合键
right[(key, x)] += 1
缺点:需要手动拼接复合键 (i+j, x)
,代码不够直观。
defaultdict(Counter)
的优势:天然支持两层结构,直接通过 right[i+j][x]
访问,代码更清晰。
7. 总结:defaultdict(Counter)
的定位
外层结构:一个字典,键是某种分类标识(如对角线)。
内层结构:每个分类标识下,自动维护一个计数器,统计子类元素(如数值)的出现次数。
本质:一种双层嵌套的计数结构,适合需要按类别统计子元素出现次数的场景。
8. 类比生活场景
想象一个超市的货架管理:
外层字典:键是货架编号(如"A1", "B2")。
内层Counter:每个货架上的商品(如"苹果"、"香蕉")的库存数量。
defaultdict(Counter)
:自动为每个新货架生成一个空的库存表(Counter),无需手动初始化。