1. 程式人生 > >有向圖或者無向圖概率dp

有向圖或者無向圖概率dp

概要:

一般形成環的用高斯消元法求解。但是遞推公式只和少數變數相關,可以考慮分離出係數。

總結:

(看完下面的例題再來看這部分)

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; }

dp求期望的題。
題意:
有n個房間,由n-1條隧道連通起來,實際上就形成了一棵樹,
從結點1出發,開始走,在每個結點i都有3種可能:
1.被殺死,回到結點1處(概率為ki)
2.找到出口,走出迷宮 (概率為ei)
3.和該點相連有m條邊,隨機走一條
求:走出迷宮所要走的邊數的期望值。

這題是樹上的概率dp,上題是有向圖,這題是無向圖,父子結點的概率互相影響。
設dp[i]表示從i號結點走出迷宮時走過邊數的概率,father[i]表示i號結點的父親結點,child表示i號結點的兒子結點

#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;
}