网上的一些博客都写到了用并查集维护,把 ( x i , y i ) (x_i,y_i) (xi,yi) 拆成点然后维护连通性的做法, 这里提供一份比较好理解的证明。
首先我们知道,若将一个坐标 ( x i , y i ) (x_i,y_i) (xi,yi) 移动后不会与别的 Rooks 发生冲突,那么我们可以一步移走;
那么如果直接移动到期望的位置后会发生冲突,那么我们就不能移一步了——需要先移一步,然后把会发生冲突的点移到对应位置上,然后再把 ( x i , y i ) (x_i,y_i) (xi,yi) 这个点移动——这样,就需要把 x i , y i x_i,y_i xi,yi 移动两步了。显然,这是这种情况下最优的方案。
于是,我们只需要知道每个棋子移动后是否会发生冲突就好了。这个我们可以用 dfs 之类的东西去做,然而这个复杂度太高了。我们希望找到一种快速而又能准确维护这个信息的方法。
首先我们来看,对于 ( x , y ) (x,y) (x,y) 和 ( i , j ) (i,j) (i,j) ,假设我们移动 ( x , y ) (x,y) (x,y) ,要使其移动后与 ( i , j ) (i,j) (i,j) 发生冲突的条件是什么。
假设我们横着移,那么有 ( x , y ) → ( x , x ) (x,y)\to (x,x) (x,y)→(x,x) ,由于数据保证了输入的 ( x , y ) (x,y) (x,y) 和 ( i , j ) (i,j) (i,j) 一定不会冲突,那么有 x ≠ i , y ≠ j x\not= i,y\not= j x=i,y=j;假设横着移动后有了冲突,那么必然有 x = j x=j x=j。
同理,假设我们竖着移且移动后产生了冲突,那么必然有 y = i y=i y=i。
然后将范围拓宽,我们把每一步次的
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi) 并入对应的集合(并查集),这个并查集实际上的意义就是行和列的集合。那么它们的祖先也就是它们共同属于的行/列。这么一来,就可以通过并查集中的find
操作判断移动后是否产生冲突了。
于是该题可以通过并查集维护的方式优雅解决。