您的位置:首页 > 其它

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

2017-11-23 10:53 465 查看

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,m,du
;
int G

,Q
,st
,tou,wei,s,t;
double f

;
bool vis

;

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: