「loj - 6295」无意识之外的捉迷藏
description
在一个有向无环图上,阿燐和阿空第 \(0\) 个时刻分别站在编号为 \(s_r, s_k\) 的节点,二人都知道双方的初始位置,对地图完全了解。
从第 \(1\) 个时刻起,每个时刻阿燐和阿空都可以选择站着不动,也可以选择移动到相邻的节点,二人每时刻的移动是同时开始的,并且不能中途改变方向。
阿燐被阿空捉住时,游戏立即结束。如果阿空一直没有捉住阿燐,第 \(t\) 个时刻结束后两人就不能再继续移动了,游戏将在第 \(t+1\) 个时刻结束。
阿空的目的是尽快捉住阿燐(捉住的定义是与阿燐同一时刻站在同一节点),而阿燐的目的是尽可能更长时间不被阿空捉住。
具体而言,若一场游戏进行了 \(t_0\) 时刻,阿燐的得分是 \(t_0\),阿空的得分是 \(-t_0\),双方都希望自己得分(或得分的期望值)更高。
我们认为在这个过程中阿燐和阿空随时都能知道对方的位置。两人在第 \(t\) 个时刻不能看出第 \(t+1\) 个时刻对方要走到哪里。
恋恋想知道,在双方最优决策的情况下,游戏结束时刻的期望值是多少。
solution
显然动态规划,只需要考虑如何转移,发现转移是一个零和博弈问题。
以下假设阿燐有 \(m\) 种选择,阿空有 \(n\) 种选择,阿空的第 \(i\) 种选择与阿燐的第 \(j\) 种选择的价值为 \(t_{i,j}\),阿燐要最大化 \(t\),阿空要最小化 \(t\)。
这个零和博弈过程其实就是纳什均衡。考虑纳什均衡的结论:双方按一定概率进行选择,如果任意一方改变策略无法使自己获利,则达到稳定态。
然后发现其实就是解线性规划。建线性规划方法较多,这里贴一种比较方便的:
设最终稳定态的价值为 \(V\),考虑阿空的策略为以概率 \(p_i(1\leq i\leq m)\) 选第 \(i\) 种选择,则有:
\[\begin{aligned} \mathrm{minimize}\ V \\ \begin{cases} \sum p_i = 1 \\ \sum p_it_{ij} \leq V \end{cases} \end{aligned} \]考虑消掉等式 \(\sum p_i = 1\)。作换元 \(x_i=\frac{p_i}{V}\),得到标准型:
\[\mathrm{maximize}\ \sum x_i \\ \sum x_it_{ij}\leq 1 \]
关于线性规划的单纯形法(参考了学长的blog),作为个人复习用。不会证明,懒得证明。
标准型:
松弛型:
其中 \(x_i\) 为非基变量,\(x_i'\) 为基变量。
定义 转轴(pivot)操作 为交换基变量 \(x'_l\) 与非基变量 \(x_e\) 的操作。
由于 \(b_l -\sum_{p\not = e} a_{lp}x_p-a_{le}x_e=x_l'\),故 \(x_e = \frac{1}{a_{le}}(b_l-\sum_{p\not = i} a_{lp}x_p - x_l')\),代入其他式子即可。
如果我们通过转轴最终得到如下的线性规划式子:
则直接令 \(x_i=0,x_i'=b_i\) 即可得到线性规划最大值 \(S\)。
分两步做:找到 \(b_i\geq 0\) 的初始解;不断转轴使得所有 \(c_i\leq 0\)。
(1)找初始解:
(2)使得所有 \(c_i\leq 0\):
找到任意 \(j\) 满足 \(c_j>0\) 且 \(c_j\) 最大(不是最大也合法,但是贪心地取最大
应该或许大概要快些)。再找到 \(i\) 满足 \(a_{ij}>0\) 且 \(\frac{b_i}{a_{ij}}\) 最小(不是最小不合法)。
转轴 \(x_i',x_j\)。可以发现这样不会使得 \(b_i < 0\) 发生。
如果找不到初始解,则无解;如果在第二步时不存在 \(a_{ij} > 0\),则无界。
code
#include <cmath>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
#define rep(i, x, n) for(int i=x;i<=n;i++)
const double EPS = 1E-9;
int dcmp(double x) {return fabs(x) <= EPS ? 0 : (x > 0 ? 1 : -1);}
double a[55][55];
void pivot(int l, int e, const int &n, const int &m) {
double t = a[l][e]; a[l][e] = 1;
rep(j, 0, m) a[l][j] /= t;
rep(i, 0, n) if( i != l && dcmp(a[i][e]) != 0 ) {
t = a[i][e], a[i][e] = 0;
rep(j, 0, m) {
if( !i && !j ) a[i][j] += t * a[l][j];
else a[i][j] -= t * a[l][j];
}
}
}
double simplex(const int &n, const int &m) {
while( true ) {
int l = 0, e = 0;
rep(j, 1, m) if( dcmp(a[0][j]) > 0 && (!e || dcmp(a[0][j] - a[0][e]) > 0) )
e = j;
if( !e ) break;
rep(i, 1, n) if( dcmp(a[i][e]) > 0 && (!l || dcmp(a[i][0]/a[i][e] - a[l][0]/a[l][e]) < 0) )
l = i;
if( !l ) return 0;
pivot(l, e, n, m);
}
return 1 / a[0][0];
}
vector<int>G[25]; double f[25][25][25]; int n;
double dp(int x, int y, int p) {
if( x == y ) return 0;
if( p == 0 ) return 1;
if( f[x][y][p] != -1 ) return f[x][y][p];
int sn = G[x].size(), sm = G[y].size();
rep(i, 1, sn) rep(j, 1, sm) dp(G[x][i - 1], G[y][j - 1], p - 1);
rep(i, 1, sn) a[i][0] = 1;
rep(j, 1, sm) a[0][j] = 1;
rep(i, 1, sn) rep(j, 1, sm)
a[i][j] = dp(G[x][i - 1], G[y][j - 1], p - 1);
a[0][0] = 0; return f[x][y][p] = simplex(sn, sm) + 1;
}
int main() {
int m, sr, sk, u, v, t;
scanf("%d%d%d%d%d", &n, &m, &sr, &sk, &t);
rep(i, 1, m) scanf("%d%d", &u, &v), G[u].push_back(v);
rep(i, 1, n) G[i].push_back(i);
rep(i, 1, n) rep(j, 1, n) rep(k, 1, t) f[i][j][k] = -1;
printf("%.3f\n", dp(sr, sk, t));
}
details
这道题由于初始 \(b_i\geq 0\) 所以不需要找初始解。无界即换元前答案为 \(0\)。