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

题意:给出一张图,图上有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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值