有向圖或者無向圖概率dp
概要:
一般形成環的用高斯消元法求解。但是遞推公式只和少數變數相關,可以考慮分離出係數。
總結:
(看完下面的例題再來看這部分)
1.這類題型一般可以先寫出原始公式然後分離出困難的變數,比如第二題的
2.將剩下的變數通過待定係數的公式帶入消去,比如例題2,
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;
}