我们可以先 \(dp\),设 \(f_{i,j,k,l}\) 和 \(g_{i,j,k,l}\)表示当前三个棋子分别在点 \(i,j,k\),目前轮到 \(l\) 走,谁胜利,最终会走多少步。
然后我们发现,变成一个有向图博弈。并且 \(l\) 是由 \(i,j,k\) 的奇偶性唯一确定的。就可以在图上直接做了。
首先我们发现,我们其实可以把初始的六个坐标编码成三个 \(i,j,k\),再编码成唯一的一个数对应图上的一个节点。并且最好不要解码出来。因为乘法编码解码的时候要取模,位运算编码占用多余空间大。
然后在可以转移到的状态之间连有向边。链式前向星存图有优势。
我们可以先处理出所有的终止状态:不同颜色点重合的、黑棋子到达终点的、某一方不能动的(也就是度数为 \(0\) 的)。
接下来,我们从所有的终止节点开始倒着 bfs。
一种做法是两次 bfs,在 bfs 的过程中,如果遇到了一定会的转移就转移上去,(例如红一定转移到红胜利的转移),而记录每个点的出度,除非所有能转移到的状态胜利方都和自己不一样,否则不会去转移。
然后再在有结果的方案中再 bfs 一遍,得到最终的步数。
但是我们发现这个其实可以一起做,因为两次都是 bfs,加入方法的判定是一致的。(因为 bfs 得到最终步数最优的证明是在第二种转移中,最后一个有贡献的转移一定是所有合法转移中步数最多的一个,符合败方的需求)我们就可以得到比较优美的做法。
贴一下个人觉得最好的 bfs 片段(by songjiaxing)
while (l <= r) {
int u = q[l++];
if (a[u].f == 1)
For(i, d[u], d[u + 1]) {
int v = G[i];
if (!a[v].f)
a[v].f = 2, a[v].g = a[u].g + 1, q[++r] = v;
else
cmin(a[v].g, a[u].g + 1);
} else
For(i, d[u], d[u + 1]) {
int v = G[i];
if (a[v].f)
continue;
a[v].g = max(a[v].g, a[u].g + 1);
if (!--a[v].d)
a[v].f = 1, q[++r] = v;
}
}
标签:步数,编码,省选,可以,我们,bfs,2023,转移,D2T1
From: https://www.cnblogs.com/jucason-xu/p/17349735.html