HDU 5001 Walk 概率DP BFS 矩阵递推 暴力

来源:互联网 发布:网络流行歌曲大全 对唱 编辑:程序博客网 时间:2024/04/30 05:55

题意:给出一张图,图上有N个点。开始时,等概率的选取起点。之后对于某个点,会从和他相连的所有点中,等概率的选取其中的一个点,继续走。一共要走D步。问,对于每个点,有多大的概率,使路径不包含该点。

思路:可以算概率DP,也能算BFS吧。赛中想到了一个点,但是没有想到删点重新建图。

          因为要求出每个点的概率,我们必须枚举每个点。对于该点,我们要求所有不到该点路径的概率。也就相当于,将该点从图上删除,其他节点到达的概率总和。

         需要注意的是,虽然该点在图上被删除了(其实是不能被访问),但每个点的度都没有变,如果改变了,会导致计算错误。

         状态转移就是对距离计数,直接看代码吧。

代码如下:

#include <cstdio>#include <algorithm>#include <cstring>using namespace std;const int MAX = 6000;int T,N,M,D;int head[MAX];int nxt[MAX];int to[MAX];int deg[MAX];int tot;double dp[10010][55];void init(){    memset(head,-1,sizeof(head));    memset(deg,0,sizeof(deg));    tot = 0;}void addedge(int u, int v){    to[tot] = v; nxt[tot] = head[u];    head[u] = tot++; deg[u]++;    to[tot] = u; nxt[tot] = head[v];    head[v] = tot++; deg[v]++;}int main(void){    //freopen("input.txt","r",stdin);    //freopen("out.txt","w",stdout);    int u,v;    scanf("%d", &T);    while(T--){        scanf("%d %d %d",&N,&M,&D);        init();        for(int i = 0 ; i < M; ++i){            scanf("%d %d", &u,&v);            addedge(u,v);        }        for(int i = 1; i <= N; ++i){            memset(dp,0.0,sizeof(dp));            for(int j = 1; j <= N; ++j) if(j != i)                dp[0][j] = 1.0 / N;            for(int d = 0; d < D; ++d){                for(int j = 1; j <= N; ++j) if(j != i)                    for(int v = head[j]; ~v; v = nxt[v])                        dp[d+1][to[v]] += dp[d][j] * (1.0 / deg[j]);            }            double ans = 0.0;            for(int j = 1; j <= N; ++j)                if(j != i) ans += dp[D][j];            printf("%.10f\n",ans);        }    }    return 0;}

同样,我们也可以用BFS的方式写转移;

#include <cstdio>#include <algorithm>#include <cstring>#include <utility>using namespace std;typedef pair<int,int> pii;const int MAX = 6000;int T,N,M,D;int head[MAX],nxt[MAX],to[MAX],deg[MAX];int tot;double dp[10010][55];bool vis[10010][55];int front, tail;pii que[10010 * 55];void init(){    memset(head,-1,sizeof(head));    memset(deg,0,sizeof(deg));    tot = 0;}void addedge(int u, int v){    to[tot] = v; nxt[tot] = head[u];    head[u] = tot++; deg[u]++;    to[tot] = u; nxt[tot] = head[v];    head[v] = tot++; deg[v]++;}int main(void){    //freopen("input.txt","r",stdin);    //freopen("out.txt","w",stdout);    int u,v;    scanf("%d", &T);    while(T--){        scanf("%d %d %d",&N,&M,&D);        init();        for(int i = 0 ; i < M; ++i){            scanf("%d %d", &u,&v);            addedge(u,v);        }        for(int i = 1; i <= N; ++i){            memset(dp,0.0,sizeof(dp));            memset(vis,false,sizeof(vis));            front = tail = 0;            for(int j = 1; j <= N; ++j)                if(j != i){                dp[0][j] = 1.0 / N;                que[tail++] = pii(0,j);                vis[0][j] = true;            }            while(front < tail){                pii tmp = que[front++];                int d = tmp.first, u = tmp.second;                if(u == i) continue;                if(d >= D) break;                for(int v = head[u]; ~v; v = nxt[v]){                    dp[d+1][to[v]] += dp[d][u] / deg[u];                    if(!vis[d+1][to[v]]){                        vis[d+1][to[v]] = true;                        que[tail++] = pii(d+1,to[v]);                    }                }            }            double ans = 0.0;            for(int j = 1; j <= N; ++j)                if(j != i) ans += dp[D][j];            printf("%.10f\n",ans);        }    }    return 0;}

这个题也可以用矩阵来求;

首先可以求出每个点到其他点的概率。这样就组成了一个矩阵。

一个点不可达,等价于其他点到它和它到其他点的概率为0。所以,对于每次枚举不可达点,我们可以将对应的矩阵的某些元素的概率设为0.这样求D次矩阵快速幂,就得到了,  长度为D,除去点i,其他点到达的概率,这样,我们在对所有的情况求和即可。

代码如下:

#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>using namespace std;int a[55][55];int deg[55];double mat[55][55];struct Matrix{    double mat[55][55];    int n, m;    void init(int n, int m){        this->n = n;        this->m = m;        memset(mat, 0, sizeof(mat));    }    Matrix operator * (const Matrix& B){        Matrix C;        C.init(n, B.m);        for(int i=0; i<n; i++){            for(int j=0; j<B.m; j++){                for(int k=0; k<m; k++){                    C.mat[i][j] += mat[i][k] * B.mat[k][j];                }            }        }        return C;    }    Matrix quick_pow(int x){        Matrix Base, ret;        Base.init(n,m);        ret.init(n,m);        for(int i=0; i<n; i++){            for(int j=0; j<m; j++){                Base.mat[i][j]=mat[i][j];                if(i==j) ret.mat[i][j]=1;            }        }        while(x){            if(x&1) ret=ret*Base;            Base=Base*Base;            x>>=1;        }        return ret;    }    void print(){        for(int i=0; i<n; i++){            for(int j=0; j<m; j++){                cout<<mat[i][j]<<" ";            } cout<<endl;        }    }};double b[55];Matrix s;int main(){    int t, n, m, d, x, y;    scanf("%d",&t);    while(t--){        scanf("%d%d%d",&n,&m,&d);        memset(a,0,sizeof a);        memset(deg,0,sizeof deg);        memset(mat,0,sizeof mat);        for(int i=0; i<m; i++){            scanf("%d%d",&x,&y);            x--, y--;            a[x][y]=a[y][x]=1;            deg[x]++, deg[y]++;        }        for(int c=0; c<n; c++){            double sum=0;            for(int i=0; i<n; i++){                for(int j=0; j<n; j++){                    if(a[i][j]) mat[i][j]=1.0/(double)deg[j];                }            }            for(int i=0; i<n; i++){                mat[c][i]=mat[i][c]=0.0;            }            s.init(n,n);            for(int j=0; j<n; j++){                for(int k=0; k<n; k++){                    s.mat[j][k]=mat[j][k];                }            }            s=s.quick_pow(d);            for(int i=0; i<n; i++){                if(i==c) continue;                for(int j=0; j<n; j++){                    if(j==i) b[j]=1.0/(double)n;                    else b[j]=0.0;                }                for(int j=0; j<n; j++){                    for(int k=0; k<n; k++){                        sum+=b[j]*s.mat[k][j];                    }                    //puts("");                }            }            printf("%.10f\n",sum);        }    }    return 0;}


0 0
原创粉丝点击