Input
数据的第1行为两个整数N和E,以空格分隔,分别表示森林中的景点数和连接相邻景点的路的条数。第2行包含两个整数C和M,以空格分隔,分别表示初始时聪聪和可可所在的景点的编号。接下来E行,每行两个整数,第i+2行的两个整数Ai和Bi表示景点Ai和景点Bi之间有一条路。所有的路都是无向的,即:如果能从A走到B,就可以从B走到A。输入保证任何两个景点之间不会有多于一条路直接相连,且聪聪和可可之间必有路直接或间接的相连。
Output
输出1个实数,四舍五入保留三位小数,表示平均多少个时间单位后聪聪会把可可吃掉。
Sample Input
【输入样例1】
4 3
1 4
1 2
2 3
3 4
【输入样例2】
9 9
9 3
1 2
2 3
3 4
4 5
3 6
4 6
4 7
7 8
8 9
Sample Output
【输出样例1】
1.500
【输出样例2】
2.167
HINT
【样例说明1】
开始时,聪聪和可可分别在景点1和景点4。
第一个时刻,聪聪先走,她向更靠近可可(景点4)的景点走动,走到景点2,然后走到景点3;假定忽略走路所花时间。
可可后走,有两种可能:
第一种是走到景点3,这样聪聪和可可到达同一个景点,可可被吃掉,步数为1,概率为 。
第二种是停在景点4,不被吃掉。概率为 。
到第二个时刻,聪聪向更靠近可可(景点4)的景点走动,只需要走一步即和可可在同一景点。因此这种情况下聪聪会在两步吃掉可可。
所以平均的步数是1* +2* =1.5步。

对于所有的数据,1≤N,E≤1000。
对于50%的数据,1≤N≤50。
先用n次Spfa预处理出path[i][j],即从i到j的第一步应该走的结点。
设f[i][j]为从猫从第i个点,老鼠在第j个点时,猫抓住老鼠所需要的时间的期望。
则有:
f[i][j] = sum {f[path[path[i][j]][j]][j`] / (deg(j) + 1)} + 1
(deg(j)为第j个结点的度。)
代码:
/************************************\
* @prob: NOI2005 cchkk *
* @auth: Wang Junji *
* @stat: Accepted. *
* @date: June. 2nd, 2012 *
* @memo: 概率动态规划、Spfa预处理 *
\************************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>
const int maxN = 1010, SIZE = 0xffff;
const double zero = 1e-12;
struct Edge
{
int v; Edge *next; Edge() {}
Edge(int v, Edge *next): v(v), next(next) {}
} *edge[maxN], _edge[maxN], *tot = _edge; double f[maxN][maxN];
int path[maxN][maxN], deg[maxN], n, m, S, T;
inline void Spfa(int S)
{
static int q[SIZE + 1], dist[maxN]; static bool marked[maxN];
memset(dist, 0x3f, sizeof dist); dist[S] = 0;
int f = 0, r = 0, u, v; Edge *p;
for (marked[q[r++] = S] = 1; f - r;)
for (p = edge[u = q[f++]], marked[u] = 0, f &= SIZE; p; p = p -> next)
if (dist[u] + 1 < dist[v = p -> v] ||
(dist[u] + 1 == dist[v] && u < path[v][S]))
{
dist[v] = dist[u] + 1, path[v][S] = u;
if (!marked[v = p -> v])
marked[q[r++] = v] = 1, r &= SIZE;
}
return;
}
void Dp(int S, int T)
{
if (S == T) {f[S][T] = 0; return;} if (f[S][T] > zero) return;
int nxt = path[S][T]; if (nxt == T) {f[S][T] = 1; return;}
//这里需要判断一下直接就能走到的情况。
nxt = path[nxt][T]; if (nxt == T) {f[S][T] = 1; return;}
double ths = 0;
for (Edge *p = edge[T]; p; p = p -> next)
Dp(nxt, p -> v), ths += f[nxt][p -> v] / (deg[T] + 1);
Dp(nxt, T); ths += f[nxt][T] / (deg[T] + 1);
f[S][T] = ths + 1; return;
}
int main()
{
freopen("cchkk.in", "r", stdin);
freopen("cchkk.out", "w", stdout);
scanf("%d%d%d%d", &n, &m, &S, &T);
while (m--)
{
int u, v; scanf("%d%d", &u, &v);
edge[u] = new (tot++) Edge(v, edge[u]); ++deg[u];
edge[v] = new (tot++) Edge(u, edge[v]); ++deg[v];
}
for (int i = 1; i < n + 1; ++i) Spfa(i);
Dp(S, T); printf("%.3lf\n", f[S][T]); return 0;
}
再贴一个考试的时候写的骗分算法:
/***************************\
* @prob: NOI2005 cchkk *
* @auth: Wang Junji *
* @stat: WA: 40 *
* @date: June. 5th, 2012 *
* @memo: Spfa、Dfs *
\***************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>
const int maxN = 1010, SIZE = 0xffff;
struct Edge
{
int v; Edge *next; Edge() {}
Edge(int v, Edge *next): v(v), next(next) {}
} *edge[maxN], _edge[maxN], *tot = _edge;
double f[maxN][maxN], ans;
int dist[maxN], deg[maxN], n, m, S, T;
inline void Bfs()
{
static int q[SIZE + 1]; static bool marked[maxN];
int f = 0, r = 0, u, v; Edge *p;
for (marked[q[r++] = S] = 1; f - r;)
for (p = edge[u = q[f++]], f &= SIZE; p; p = p -> next)
if (!marked[v = p -> v])
dist[v] = dist[u] + 1, marked[q[r++] = v] = 1, r &= SIZE;
for (int i = 1; i < n + 1; ++i) if (i - S) ++(--dist[i] >>= 1);
return;
}
void Dp(int u, int step)
{
if (f[u][step] < 1e-7) return;
if (step >= dist[u])
{
ans += f[u][step] * dist[u];
return;
}
for (Edge *p = edge[u]; p; p = p -> next)
{
f[p -> v][step + 1] = f[u][step] / (deg[u] + 1);
Dp(p -> v, step + 1);
}
f[u][step + 1] = f[u][step] / (deg[u] + 1);
Dp(u, step + 1); return;
}
int main()
{
freopen("cchkk.in", "r", stdin);
freopen("cchkk.out", "w", stdout);
scanf("%d%d%d%d", &n, &m, &S, &T);
while (m--)
{
int u, v; scanf("%d%d", &u, &v);
edge[u] = new (tot++) Edge(v, edge[u]);
edge[v] = new (tot++) Edge(u, edge[v]);
++deg[u]; ++deg[v];
}
Bfs();
if (dist[T] <= 1)
{
printf("%.3lf\n", (double)(S != T));
return 0;
}
f[T][1] = 1; Dp(T, 1); printf("%.3lf\n", ans);
return 0;
}
/*
贪心,首先算出猫从初始位置走到所有点需要的时间,然后假定猫在最后追到老鼠的时候走的一定是最短路径(即不存在猫追老鼠兜圈子的情况),这样做可以得40分。
*/