#深度优先搜索 #博弈论
需要一点细节的博弈论好题。
## 题目大意
有 $3\times 3$ 的棋盘,第 $i$ 行第 $j$ 列上面的数为 $a_{i,j}$。高桥和奥吉轮流下棋,如果在下棋的过程中其中一个人有三个子连成了一条长度为 $3$ 线,那么这个人就直接赢了。如果到最后都没分出胜负,那么就按照下的位置上面的数的和为标准来判断。两个人都会选择最优策略。
## 思路
首先是这道题的中心思想,非常重要,一定要默念三遍:如果对面有一种下棋方法必输,那么我就是必赢的;如果对面所有的下棋方法都必赢,那么我就必输。
接下来,我们可以采用 $\mathcal O(9!)$ 的大爆搜,这就代表着,只要我们细心地对着上面的结论进行模拟,那么就可以一遍过!
## 代码
```kotlin
import java.util.*
val cin = Scanner(System.`in`)
const val n = 3
val a = Array(n + 1) { LongArray(n + 1) } // 位置上面的数
val f = Array(n + 1) { LongArray(n + 1) } // 每一个位置被谁给标记了
var s1 = 0L // 高桥的和
var s2 = 0L // 奥吉的和
fun check(): Long { // 判断那个人连成了一条线
for (i in 1..n) { // 遍历每一行和每一列
if (f[i][1] != 0L && f[i][1] == f[i][2] && f[i][2] == f[i][3]) { // 当前行的赢了
return f[i][1] // 返回赢了的人
} else if (f[1][i] != 0L && f[1][i] == f[2][i] && f[2][i] == f[3][i]) { // 当前列的赢了
return f[1][i] // 返回赢了的人
}
}
if (f[1][1] != 0L && f[1][1] == f[2][2] && f[2][2] == f[3][3]) { // 右斜线
return f[1][1]
} else if (f[1][3] != 0L && f[1][3] == f[2][2] && f[2][2] == f[3][1]) { // 左斜线
return f[1][3]
}
return 0L // 没有人赢
}
fun dfs(x: Long): Boolean { // 大爆搜环节
if (check() != 0L) { // 如果有人连成了一条线
return check() - 1L != x % 2L // 判断当前人是否输了
// 这里可能比较抽象
// 因为当前人还没有下棋,因此需要判断的是当前人有没有输
// check() - 1L:高桥赢了为 0,奥吉赢了为 1
// x % 2L:当前为高桥为 1,当前为奥吉为 0
// 需要使用不等于号,因为假设当前为奥吉,刚才高桥赢了,
// 那么表达式就等同于 0 != 0,则 false
}
if (x > 9) { // 没有决出胜负
return s2 > s1 // 按照得分来判断
// 注意需要反过来,因为
// 刚才下的是高桥,我们需要
// 判断奥吉有没有赢
}
var b = false // 判断对面有没有必输的可能
for (i in 1..n) { // 枚举行
for (j in 1..n) { // 枚举列
if (f[i][j] == 0L) { // 如果还没有被下
if (x % 2 != 0L) s1 += a[i][j] else s2 += a[i][j] // 累加到得分里面
f[i][j] = if (x % 2 != 0L) 1 else 2 // 打上标记,高桥为 1,奥吉为 2
b = b || !dfs(x + 1) // 继续搜索,看对面有没有必输的可能(这里可以剪枝)
if (x % 2 != 0L) s1 -= a[i][j] else s2 -= a[i][j] // 去掉加上的得分
f[i][j] = 0L // 清空标记
}
}
}
return b // 是否必赢
}
fun main() {
for (i in 1..n) {
for (j in 1..n) {
a[i][j] = cin.nextLong()
}
}
println(if (dfs(1)) "Takahashi" else "Aoki")
}
```