1. 程式人生 > 實用技巧 >【loj - 6295】無意識之外的捉迷藏

【loj - 6295】無意識之外的捉迷藏

目錄


description

在一個有向無環圖上,阿燐和阿空第 \(0\) 個時刻分別站在編號為 \(s_r, s_k\) 的節點,二人都知道雙方的初始位置,對地圖完全瞭解。

從第 \(1\) 個時刻起,每個時刻阿燐和阿空都可以選擇站著不動,也可以選擇移動到相鄰的節點,二人每時刻的移動是同時開始的,並且不能中途改變方向。

阿燐被阿空捉住時,遊戲立即結束。如果阿空一直沒有捉住阿燐,第 \(t\) 個時刻結束後兩人就不能再繼續移動了,遊戲將在第 \(t+1\) 個時刻結束。

阿空的目的是儘快捉住阿燐(捉住的定義是與阿燐同一時刻站在同一節點),而阿燐的目的是儘可能更長時間不被阿空捉住。

具體而言,若一場遊戲進行了 \(t_0\) 時刻,阿燐的得分是 \(t_0\),阿空的得分是 \(-t_0\),雙方都希望自己得分(或得分的期望值)更高。

我們認為在這個過程中阿燐和阿空隨時都能知道對方的位置。兩人在第 \(t\) 個時刻不能看出第 \(t+1\) 個時刻對方要走到哪裡。

戀戀想知道,在雙方最優決策的情況下,遊戲結束時刻的期望值是多少。

problem link。


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),作為個人複習用。不會證明,懶得證明。

標準型:

\[\begin{aligned} \mathrm{maximize}\sum & c_ix_i \\ \sum a_{ij}x_j & \leq b_i \\ x_i & \geq 0 \end{aligned} \]

鬆弛型:

\[\begin{aligned} \mathrm{maximize}\sum & c_ix_i \\ b_i - \sum a_{ij}x_j & = x'_{i} \\ x_i,x'_i & \geq 0 \end{aligned} \]

其中 \(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')\),代入其他式子即可。

如果我們通過轉軸最終得到如下的線性規劃式子:

\[\begin{aligned} \mathrm{maximize}\sum & c_ix_i + S(c_i\leq0) \\ b_i - \sum a_{ij}x_j & = x'_{i}(b_i\geq 0) \\ x_i,x'_i & \geq 0 \end{aligned} \]

則直接令 \(x_i=0,x_i'=b_i\) 即可得到線性規劃最大值 \(S\)


分兩步做:找到 \(b_i\geq 0\) 的初始解;不斷轉軸使得所有 \(c_i\leq 0\)

(1)找初始解:

上面的 blog 裡提到了一種準確的方法 但是好像沒什麼人寫

有一個好寫點的隨機演算法:隨機取 \(b_i < 0\),再隨機取 \(a_{ij}<0\),轉軸 \(x'_i,x_j\)。這樣至少能夠保證轉軸後 \(b_i \geq 0\) (顯然這個可能會WA)

(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\)