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


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分。

*/


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值