bzoj1415 [Noi2005]聪聪和可可 (概率与期望)

来源:互联网 发布:咏春拳 知乎 编辑:程序博客网 时间:2024/05/19 11:50

Description

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。

Source

[Submit][Status][Discuss]


分析:
首先聪聪的决策是有计划性的,而且与可可的位置有关
所以我们可以用n次bfs预处理出p[i][j]表示聪聪在i点可可在j点,聪聪下一步要到达的点
G[i][j]表示和i相连的编号第j大的结点
因为在多种方案的前提下,聪聪会优先选择编号小的点
所以我们在bfs的时候就选择编号小的点优先入队,这样就可以保证最后记录的是最短路上的编号最小决策

f[i][j]表示聪聪在i,可可在j,聪聪捉到可可的期望时间

初始化:

  • i=j,f[i][j]=0
  • p[i][j]==j || p[p[i][j]][j]==j,f[i][j]=1

转移方程:

f[i][j]=( ( Σ{ f [ p[p[i][j]][j] ][ G[j][k] ] } + f [ p[p[i][j]][j] ][ j ] ) / (du[j]+1) )+1

因为我们已经与处理过能过直接抓到可可的状态
所以需要上式转移的情况都是聪聪要走两步,可可有du+1种选择的情况

期望=总情况/情况总数

现在我们还有一个问题,就是我们要怎么转移呢
显然简单的从1到n这样的转移是没有前途的(原则:从稳定状态转移)
因为dp是在一个图上的,所以立马就想到了记忆化搜索

tip

注意数组类型,不要把int和double混用

//这里写代码片#include<cstdio>#include<cstring>#include<iostream>#include<cmath>#include<algorithm>using namespace std;const double INF=1e9;const int N=1005;int p[N][N],n,m,du[N];int G[N][N],Q[N],st[N],tou,wei,s,t;double f[N][N];bool vis[N][N];void bfs(int s)     //预处理{    tou=wei=0;    for (int i=1;i<=st[s];i++) Q[++wei]=G[s][i],p[s][G[s][i]]=G[s][i];    p[s][s]=s;    while (tou<wei)    {        int now=Q[++tou];        for (int i=1;i<=st[now];i++)        {            int v=G[now][i];            if (p[s][v]==-1)            {                p[s][v]=p[s][now];                Q[++wei]=v;            }        }    }}double dfs(int i,int j){    if (vis[i][j]) return f[i][j];    vis[i][j]=1;    double ans=0;    for (int k=1;k<=st[j];k++)        ans+=dfs(p[p[i][j]][j],G[j][k]);    //聪聪一定会移动两步      ans+=dfs(p[p[i][j]][j],j);              //可可不动     ans=ans/(double)(du[j]+1.0);    ans=ans+1.0;    return f[i][j]=ans;}void solve(){    int i,j;    memset(vis,0,sizeof(vis));    for (i=1;i<=n;i++) for (j=1;j<=n;j++) {        f[i][j]=INF;         if (j==p[i][j]||j==p[p[i][j]][j]) f[i][j]=1.0,vis[i][j]=1;   //一次就可以到达     }    for (int i=1;i<=n;i++) f[i][i]=0.0,vis[i][i]=1;     dfs(s,t);    printf("%.3lf",f[s][t]);}int main(){    scanf("%d%d",&n,&m);    scanf("%d%d",&s,&t);    for (int i=1;i<=m;i++)    {        int u,w;        scanf("%d%d",&u,&w);        G[u][++st[u]]=w; G[w][++st[w]]=u;        du[u]++; du[w]++;          //度     }    for (int i=1;i<=n;i++) sort(G[i]+1,G[i]+1+st[i]);     //保证先到达编号小的节点     memset(p,-1,sizeof(p));    for (int i=1;i<=n;i++)        bfs(i);                                           //聪聪的位置     solve();    return 0;}
原创粉丝点击