[BZOJ 1415][NOI 2005]聪聪和可可(SPFA+概率DP)

来源:互联网 发布:华大基因 知乎 编辑:程序博客网 时间:2024/05/16 17:50

题目链接

http://www.lydsy.com/JudgeOnline/problem.php?id=1415

思路

根据题目要求,聪聪每次从旧点u移动到新点vvu到可可当前所在的点T的最短路径上,与u相邻的那个点,并且若存在多条最短路径,v的编号一定是所有满足条件的v中最小的。而且在一步之内,聪聪可以先跳到当前点u的一个后继u上,若u不是T,则聪聪可以再从u跳到新的u的后继nextu上,此时若nextu就是T,则可可被聪聪吃掉了,否则可可可以留在原地T不动,或者移动到T相邻的点上,概率都是一样的。
那么我们可以根据这些特点,首先通过n次SPFA,找出pos[i][j]数组,表示点i到点j的所有最短路中,与点i相邻,且编号最小的点。假如聪聪在点i,显然他会在下一时间跳到pos[i][T](跳一次就抓住了可可)或者pos[pos[i][T][T](跳一次抓不住可可)。我们通过DFS,找出聪聪在点u,可可在点v时,聪聪抓住可可的期望步数。这个DFS就是模拟聪聪和可可在下一时刻分别跑到了什么地方,将所有可能的后继状态的期望加起来并求平均值,即可得到本时刻的期望步数。简单表示当前状态的期望步数为stepu,v则有:

stepu,v=vvsteppos[pos[u][v]][v],v

显然这个DFS可以通过记忆化来优化,优化后这个做法就能AC

代码

#include <iostream>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <algorithm>#define MAXE 2100#define MAXV 1100using namespace std;struct edge{    int u,v,w,next;}edges[MAXE];int head[MAXV],nCount=0;void AddEdge(int U,int V,int W){    edges[++nCount].u=U;    edges[nCount].v=V;    edges[nCount].w=W;    edges[nCount].next=head[U];    head[U]=nCount;}int q[MAXE*2],dist[MAXV];bool inQueue[MAXV];int pos[MAXV][MAXV]; //pos[i][j]=i到j的最短路上与i相邻且编号最小的点编号double f[MAXV][MAXV]; //f[i][j]=聪聪在点i,可可在点j,聪聪抓住可可所需期望步数void SPFA(int S){    memset(inQueue,false,sizeof(inQueue));    memset(dist,0x3f,sizeof(dist));    int h=0,t=0;    dist[S]=0;    for(int p=head[S];p!=-1;p=edges[p].next)    {        int v=edges[p].v;        q[t++]=v;        dist[v]=1;        pos[S][v]=v; //!!!!!        inQueue[v]=true;    }    while(h<t)    {        int u=q[h++];        inQueue[u]=false;        for(int p=head[u];p!=-1;p=edges[p].next)        {            int v=edges[p].v;            if(dist[u]+edges[p].w<dist[v])            {                dist[v]=dist[u]+edges[p].w;                pos[S][v]=pos[S][u];                if(!inQueue[v])                {                    q[t++]=v;                    inQueue[v]=true;                }            }            else if(dist[u]+edges[p].w==dist[v]&&pos[S][u]<pos[S][v])            {                pos[S][v]=pos[S][u];                if(!inQueue[v])                {                    q[t++]=v;                    inQueue[v]=true;                }            }        }    }}void DFS(int u,int v) //求f[u][v]{    if(f[u][v]>=0) return;    int nextu=pos[pos[u][v]][v]; //一般情况下u在下一时刻走到的位置    int cnt_child=0; //v的后继个数    double sum=0; //sum=nextu到所有v的后继的期望步数之和    if(nextu==v) //直接在下一时刻抓到了可可,那么吃到可可的期望步数就是1次    {        f[u][v]=1;        return;    }    for(int p=head[v];p!=-1;p=edges[p].next)    {        int nextv=edges[p].v;        DFS(nextu,nextv);        sum+=f[nextu][nextv];        cnt_child++;    }    DFS(nextu,v);    sum+=f[nextu][v];    cnt_child++;    f[u][v]=sum/cnt_child+1; //u到nextu走了一步,所以要加1}int n,m;int main(){    int S,T;    scanf("%d%d%d%d",&n,&m,&S,&T);    memset(head,-1,sizeof(head));    memset(f,-1,sizeof(f));    for(int i=1;i<=m;i++)    {        int u,v;        scanf("%d%d",&u,&v);        AddEdge(u,v,1);        AddEdge(v,u,1);    }    for(int i=1;i<=n;i++) //n次SPFA预处理出所有的pos[i][j]    {        f[i][i]=0; //相遇直接吃掉,无需移动        pos[i][i]=i;        SPFA(i);    }    DFS(S,T);    printf("%.3lf\n",f[S][T]);    return 0;}
1 0
原创粉丝点击