题目重点:与所有其他选手的胜负都能推导时,排名才唯一。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 严格胜负无平局且真实总排名与所有结果一致,N选手编号0..N−1。
- 矛盾环拒绝,不通过“谁都能赢谁”给假定排名。返回player→1based rank。
解题路线
- reach[a][b]代表a可以推导胜b,Floyd传递闭包。对i若所有j≠i都满足reach[i][j]或reach[j][i]则相对关系完整。
- 排名=胜过i的人数+1。若存在不相关j,可在至少两种合法总序中调换其相对位置,不能确定i唯一名次。
- 检查reach[i][i]发现环;输入如果真实体育赛有循环胜负,应改变为分数/赛制排名定义,这个总序合同不再适用。
手动走一遍
0>1>2,三人排名1/2/3都可确定。0>2、1>2,2确定第三,0与1相对未知,两者不确定。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
def rankings(count,wins):
reach=[[False]*count for _ in range(count)]
for winner,loser in wins:
if not (0<=winner<count and 0<=loser<count):raise ValueError("unknown player")
reach[winner][loser]=True
for k in range(count):
for i in range(count):
if reach[i][k]:
for j in range(count):reach[i][j]|=reach[k][j]
if any(reach[i][i] for i in range(count)):raise ValueError("cyclic results inconsistent with total rank")
return {i:1+sum(reach[j][i] for j in range(count)) for i in range(count)
if all(i==j or reach[i][j] or reach[j][i] for j in range(count))}
复杂度
Floyd O(N³)时间/O(N²)空间;稀疏可每源DFS/BFS O(N(N+M)),输出O(N)。
易错点与追问
容易失分的边界
- 只统计直接胜负数会漏传递。
- 可确定不等于当前排序算法任意排列的位置,后者可能不是唯一。
继续深挖
- 允许平局形成等价组时rank要怎样定义?
- 新增一场结果如何增量更新闭包并检测矛盾?
