HDU 4035 Maze (很好的概率DP)

做了有些概率和期望的题目了,现在觉得大致有那么两种办法。【说白了还是要靠数学

一、直接利用组合计数求解。

二、通过状态转移列出方程来,如果能递推就直接DP,不能的话,通过对式子处理,找出式子之间的关系和结构上的特点,通过待定系数来进行迭代,最后求出结果。

。。。再有的话就是用容斥啊状压啊什么的【估计自己也不一定想得到


言归正传来说题吧,我把自己的思路写在注释里了。

参考题解:点击打开链接


/*
 * 设dp[i]为从i出发成功逃生的步数的期望值。
 * 那么dp[i] = ki * dp[1] + (1 - ki - ei) / m * sigma(dp[j] + 1);(m为i的度数,j是i的相邻节点)
 * 整理得, dp[i] = ki * dp[1] + (1 - ki - ei) / m * sigma(dp[j]) + (1 - ki - ei) / m。
 *  
 * 高斯消元显然会超时,所以要考虑父节点和子节点的关系。
 *
 * 叶子节点:dp[i] = ki * dp[1] + (1 - ki - ei) * dp[father[i]] + 1 - ki - ei.
 * 非叶子节点:dp[i] = ki * dp[1] + (1 - ki - ei) / m * sigma(dp[child[i]]) + (1 - ki - ei) / m * dp[father[i]] + (1 - ki - ei) / m
 *         对于dp[child[i]],可以用dp[i]表示。
 * 这样一来,dp[i] 可以表示成 A * dp[1] + B * dp[father[i]] + C
 * 然后从叶子节点不断往上推,直到1节点为止。
 */
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<cstdlib>

using namespace std;
const int maxn = 10005;
const double eps = 1e-9;

struct Nod{
    int b,next;
    void init(int b,int next){
        this->b = b;
        this->next = next;
    }
}buf[maxn<<2];
int len,head[maxn];

int n,deg[maxn],M[maxn],num[maxn];
double A[maxn],B[maxn],C[maxn],K[maxn],E[maxn];
queue<int> que;

void init(){
    memset(num,-1,sizeof(num));
    memset(head,-1,sizeof(head));
    memset(deg,0,sizeof(deg));
    len = 0;
}

void DFS(int x,int &cnt){
    num[x] = cnt++;
    for(int i = head[x] ; i != -1 ;i = buf[i].next){
        int v = buf[i].b;
        if(num[v] != -1) continue;
        DFS(v,cnt);
    }
}

void solve(){
    int cnt = 0;
    DFS(1,cnt);

    for(int i = 2; i <= n ; i++)
        if(deg[i] == 1) que.push(i);
    while(!que.empty()){
        int v = que.front();que.pop();
        //cout<<v;
        deg[v]--;
        A[v] = K[v];B[v] = (1 - K[v] - E[v]) / M[v] ; C[v] = 1 - K[v] - E[v];
        //cout<<v<<" : "<<A[v]<<" "<<B[v]<<" "<<C[v]<<endl;
        double t = 1,tmp = B[v];
        for(int i = head[v] ; i != -1 ; i = buf[i].next){
            int u = buf[i].b;
            if(num[u] < num[v]){
                //cout<<" : "<<u<<endl;
                deg[u]--;
                if(u != 1 && deg[u] == 1) que.push(u);
                if(u == 1 && deg[u] == 0) que.push(1);
                continue;
            }
            deg[u]--;
            A[v] += tmp * A[u];
            C[v] += tmp * C[u];
            t -= tmp * B[u];
        }
        A[v] /= t;B[v] /= t;C[v] /= t;
        //cout<<A[v]<<" "<<B[v]<<" "<<C[v]<<endl;
    }
    if(fabs(1.0 - A[1]) < eps) printf("impossible\n");
    else printf("%.6f\n",C[1] / (1 - A[1]));
}

void add_Edge(int a,int b){
    buf[len].init(b,head[a]);head[a] = len++;
    buf[len].init(a,head[b]);head[b] = len++;
    deg[a]++;deg[b]++;
}

int main(){
    int cas;
    scanf("%d",&cas);
    for(int T = 1; T <= cas; T++){
        init();
        scanf("%d",&n);
        int a,b;
        for(int i=1;i<n;i++){
            scanf("%d%d",&a,&b);
            add_Edge(a,b);
        }
        memcpy(M,deg,sizeof(deg));
        for(int i=1;i<=n;i++){
            scanf("%lf%lf",&K[i],&E[i]);
            K[i] /= 100;E[i] /= 100;
        }
        printf("Case %d: ",T);
        solve();
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值