【概率动态规划】【NOI2005】聪聪和可可

来源:互联网 发布:配置linux网络设置的ip 编辑:程序博客网 时间:2024/05/17 09:03


Input数据的第1行为两个整数N和E,以空格分隔,分别表示森林中的景点数和连接相邻景点的路的条数。第2行包含两个整数C和M,以空格分隔,分别表示初始时聪聪和可可所在的景点的编号。接下来E行,每行两个整数,第i+2行的两个整数Ai和Bi表示景点Ai和景点Bi之间有一条路。所有的路都是无向的,即:如果能从A走到B,就可以从B走到A。输入保证任何两个景点之间不会有多于一条路直接相连,且聪聪和可可之间必有路直接或间接的相连。Output输出1个实数,四舍五入保留三位小数,表示平均多少个时间单位后聪聪会把可可吃掉。Sample Input【输入样例1】4 31 41 22 33 4【输入样例2】9 99 31 22 33 44 53 64 64 77 88 9Sample Output【输出样例1】1.500【输出样例2】2.167HINT【样例说明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分。*/