有向图或者无向图概率dp

来源:互联网 发布:罗马音发音软件 编辑:程序博客网 时间:2024/06/05 14:10

概要:

一般形成环的用高斯消元法求解。但是递推公式只和少数变量相关,可以考虑分离出系数。

总结:

(看完下面的例题再来看这部分)

1.这类题型一般可以先写出原始公式然后分离出困难的变量,比如第二题的dp[1] , dp[father[i]] , 都是很难处理的变量,就可以把它们作为待定系数的变量
2.将剩下的变量通过待定系数的公式带入消去,比如例题2,dp[child[i]](Ajdp[1]+Bjdp[i]+Cj) (j=child[i])带入消去,从而找到关于系数A,B,C的递推公式
3.写出结果公式*用递推出的系数求出结果

例题1:HDU4405
题意:有三个骰子,分别有k1,k2,k3个面。
每次掷骰子,如果三个面分别为a,b,c则分数置0,否则加上三个骰子的分数之和。
当分数大于n时结束。求游戏的期望步数。初始分数为0

设dp[i]表示达到i分时到达目标状态的期望,pk为投掷k分的概率,p0为回到0的概率
则dp[i]=∑(pk*dp[i+k])+dp[0]*p0+1;
都和dp[0]有关系,而且dp[0]就是我们所求,为常数
设dp[i]=A[i]*dp[0]+B[i];
代入上述方程右边得到:
dp[i]=∑(pk*A[i+k]*dp[0]+pk*B[i+k])+dp[0]*p0+1
=(∑(pk*A[i+k])+p0)dp[0]+∑(pk*B[i+k])+1;
明显A[i]=(∑(pk*A[i+k])+p0)
B[i]=∑(pk*B[i+k])+1
先递推求得A[0]和B[0].
那么 dp[0]=B[0]/(1-A[0]);

#include <set>#include <map>#include <queue>#include <vector>#include <math.h>#include <iostream>#include <stdio.h>#include <algorithm>#include <string.h>using  namespace  std;#define ff first#define ss second#define pb push_back#define ll long long#define mod 1000000007#define ull unsigned long long#define mst(ss,b) memset(ss,b,sizeof(ss));#define pl(x) cout << #x << "= " << x << endl;const int inf = 0x3f3f3f3f;const int N = 1e5+10;double dp[N];int n, m;int path[N], jump[N];int  main(){    while(~scanf("%d%d", &n, &m)){        if(n == 0 && m == 0)break;        mst(dp, 0);        mst(path, -1);        mst(jump, -1);        for(int i=1; i<=m; i++){            int u, v;            scanf("%d%d", &u, &v);            path[u] = v;        }        for(int i=n; i>=1; i--){            int j = path[i];            if(j == -1)continue;            if(jump[j] != -1)jump[i] = jump[j];            else jump[i] = j;        }        for(int i=n-1; i>=0; i--){            if(jump[i] != -1)dp[i] = dp[jump[i]];            else{                for(int j=1; j<=6; j++)                    dp[i] += dp[i+j];                dp[i] = dp[i]/6.0+1;            }        }        printf("%.4f\n", dp[0]);    }    return 0;}

例题2:HDU 4035

dp求期望的题。
题意:
有n个房间,由n-1条隧道连通起来,实际上就形成了一棵树,
从结点1出发,开始走,在每个结点i都有3种可能:
1.被杀死,回到结点1处(概率为ki)
2.找到出口,走出迷宫 (概率为ei)
3.和该点相连有m条边,随机走一条
求:走出迷宫所要走的边数的期望值。

这题是树上的概率dp,上题是有向图,这题是无向图,父子结点的概率互相影响。
设dp[i]表示从i号结点走出迷宫时走过边数的概率,father[i]表示i号结点的父亲结点,child表示i号结点的儿子结点

递推公式求法可参考kuangbin的题解
http://www.cnblogs.com/kuangbin/archive/2012/10/03/2711108.html

#include <bits/stdc++.h>using  namespace  std;#define ff first#define ss second#define pb push_back#define ll long long#define mod 1000000007#define ull unsigned long long#define mst(ss,b) memset(ss,b,sizeof(ss));#define dbg(x) cout << #x << "= " << x << endl;typedef pair <int, int> pii;const int inf = 0x3f3f3f3f;const int N = 1e4+5;const double eps = 1e-12; //这题需要开小点double k[N], e[N];vector<int>E[N];double A[N], B[N], C[N];int n;bool dfs(int u, int pre){    int m = E[u].size();    A[u] = k[u];    B[u] = (1-k[u]-e[u])/m;    C[u] = 1-k[u]-e[u];    double tmp = 1.0;    for(int i=0; i<m; i++){        int v = E[u][i];        if(v == pre)continue;        if(!dfs(v, u))return 0;        A[u] += (1-k[u]-e[u])/m*A[v];        C[u] += (1-k[u]-e[u])/m*C[v];        tmp -= (1-k[u]-e[u])/m*B[v];    }    if(fabs(tmp) < eps)return 0;    A[u] /= tmp;    B[u] /= tmp;    C[u] /= tmp;    return 1;}int  main(){    int T;    scanf("%d", &T);    for(int kase=1; kase<=T; kase++){        printf("Case %d: ", kase);        scanf("%d", &n);        for(int i=1; i<=n; i++)E[i].clear();        for(int i=1; i<n; i++){            int u, v;            scanf("%d%d", &u, &v);            E[u].pb(v);            E[v].pb(u);        }        for(int i=1; i<=n; i++){            int x, y;            scanf("%d%d", &x, &y);            k[i] = x/100.0;            e[i] = y/100.0;        }        if(dfs(1, -1) && fabs(1-A[1]) > eps)            printf("%.6f\n", C[1]/(1-A[1]));        else puts("impossible");    }    return 0;}
0 0
原创粉丝点击