1. 程式人生 > >[NOI 2005]聰聰和可可

[NOI 2005]聰聰和可可

隨機 老鼠 memset pre 我們 () code ref bits

Description

題庫鏈接

一只貓和一只老鼠在一張 \(n\) 個節點和 \(m\) 條邊的無向圖上,初始位置不同。對於每一時刻,貓會先走,它走的方向為靠近老鼠的方向;若多個節點可選,則選字典序最小的那個。同時若走出這步後沒有抓到老鼠,則可按同樣方式再走一步;接著老鼠會等概率的停在原地或者隨機走向一個相鄰的節點。問抓到老鼠的期望時間。

\(1\leq n,m\leq 1000\)

Solution

首先註意到這樣一句話“若走出這步後沒有抓到老鼠,則可按同樣方式再走一步”,顯然是能夠保證貓一定能抓到老鼠。並且貓和老鼠兩個所處位置的狀態是具有層次性的。

容易發現老鼠的移動是沒有規律的,即是隨機的。而貓的動作是有規律的。

我們可以事先預處理出一個 \(pre_{u,v}\) 數組,表示貓在 \(u\) 處,老鼠在 \(v\) 處時,貓下一個選擇要走的節點是哪一個,可以用 \(n\)\(SPFA\) 預處理出來,由於邊數和點數是同階的,復雜度可以得到保障。

我們可以設出一個 \(dp\) 數組 \(f_{u,v}\) 表示貓在 \(u\) 處,老鼠在 \(v\) 處時期望走的時間為 \(f_{u,v}\)

首先顯然當 \(u=v\) 時, \(f_{u,v}=0\) ;其次若 \(pre_{u,v}=v\) 即走出一步抓到老鼠或者 \(pre_{pre_{u,v},v}=v\) 即走出兩步抓到老鼠, \(f_{u,v}=1\)

這時剩下的情況就是老鼠會移動。

由於貓會先走,貓移動之後老鼠再走;顯然貓移動結束後停在的位置為 \(pre_{pre_{u,v},v}\)

設節點 \(v\) 以及和 \(v\) 相鄰的節點的集合為 \(\mathbb{V}\) ,節點 \(v\) 的度數為 \(degree_v\) 。顯然答案就是 \[f_{u,v}=\frac{\sum\limits_{x\in\mathbb{V}}f_{pre_{pre_{u,v},v},x}}{degree_v+1}+1\]

記憶化搜索實現。

Code

//It is made by Awson on 2018.2.24
#include <bits/stdc++.h>
#define LL long long #define dob complex<double> #define Abs(a) ((a) < 0 ? (-(a)) : (a)) #define Max(a, b) ((a) > (b) ? (a) : (b)) #define Min(a, b) ((a) < (b) ? (a) : (b)) #define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b)) #define writeln(x) (write(x), putchar('\n')) #define lowbit(x) ((x)&(-(x))) using namespace std; const int N = 1000; void read(int &x) { char ch; bool flag = 0; for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == '-')) || 1); ch = getchar()); for (x = 0; isdigit(ch); x = (x<<1)+(x<<3)+ch-48, ch = getchar()); x *= 1-2*flag; } void print(LL x) {if (x > 9) print(x/10); putchar(x%10+48); } void write(LL x) {if (x < 0) putchar('-'); print(Abs(x)); } int n, m, s, t, u, v; struct tt {int to, next; }edge[(N<<1)+5]; int path[N+5], top, degree[N+5]; int pre[N+5][N+5]; double f[N+5][N+5]; queue<int>Q; int vis[N+5], dist[N+5]; void add(int u, int v) {edge[++top].to = v, edge[top].next = path[u], path[u] = top, ++degree[u]; } void get_pre(int x) { memset(dist, 127/3, sizeof(dist)); dist[x] = 0, vis[x] = 1; Q.push(x); while (!Q.empty()) { int u = Q.front(); Q.pop(); vis[u] = 0; for (int i = path[u]; i; i = edge[i].next) if (dist[edge[i].to] > dist[u]+1) { dist[edge[i].to] = dist[u]+1; if (!vis[edge[i].to]) vis[edge[i].to] = 1, Q.push(edge[i].to); if (u == x) pre[x][edge[i].to] = edge[i].to; else pre[x][edge[i].to] = pre[x][u]; }else if (dist[edge[i].to] == dist[u]+1 && pre[x][edge[i].to] > pre[x][u]) pre[x][edge[i].to] = pre[x][u]; } } double dp(int s, int t) { if (s == t) return 0; int nex = pre[pre[s][t]][t]; if (pre[s][t] == t || nex == t) return f[s][t] = 1; if (f[s][t] != 0) return f[s][t]; double k = 1/(1.0*(degree[t]+1)); f[s][t] = 1+dp(nex, t)*k; for (int i = path[t]; i; i = edge[i].next) f[s][t] += k*dp(nex, edge[i].to); return f[s][t]; } void work() { read(n), read(m); read(s); read(t); for (int i = 1; i <= m; i++) read(u), read(v), add(u, v), add(v, u); for (int i = 1; i <= n; i++) get_pre(i); printf("%.3lf\n", dp(s, t)); } int main() { work(); return 0; }

[NOI 2005]聰聰和可可