1. 程式人生 > >藍書(演算法競賽進階指南)刷題記錄——CH3802 綠豆蛙的歸宿(DAG期望DP)

藍書(演算法競賽進階指南)刷題記錄——CH3802 綠豆蛙的歸宿(DAG期望DP)

題目:CH3802.

題目大意:給定一張有向無環圖,一直蛙要從點1走到點n,它每次會等概率從一個點經過一條出邊走到下一個點,求從點1走到點n的期望路徑長度.

我們很容易看出這是一個期望DP.

那麼我們設狀態f[i]為從點1到點i時的期望路徑長度.

但是我們發現狀態轉移方程就十分不自然了,寫起來總感覺很彆扭.

所以我們換一個狀態,設狀態f[i]為從點i到點N的期望路徑長度.

我們設點i的出邊數量為k_i,所有出邊的終點為y_j,邊權為v_j,那麼方程就是:f[i]=\sum_{j=1}^{k_i}(f[y_j]+v_j).

那麼我們只需要建一張反圖,在反圖上跑DP即可.

程式碼如下:

#include<bits/stdc++.h>
  using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=100000;
int n,m;
struct side{
  int y,next,v;
}e[N*2+9];
int lin[N+9],top,deg[N+9],out[N+9];
queue<int>q;
double f[N+9];
void ins(int x,int y,int v){
  e[++top].y=y;e[top].v=v;
  e[top].next=lin[x];
  lin[x]=top;
}
Abigail into(){
  scanf("%d%d",&n,&m);
  int x,y,v;
  for (int i=1;i<=m;i++){
    scanf("%d%d%d",&x,&y,&v);
    ins(y,x,v);
    deg[x]++;out[x]++;
  }
}
Abigail work(){
  q.push(n);
  while (!q.empty()){
    int t=q.front();q.pop();
    for (int i=lin[t];i;i=e[i].next){
      f[e[i].y]+=(f[t]+1.0*e[i].v)/deg[e[i].y];
      out[e[i].y]--;
      if (!out[e[i].y]) q.push(e[i].y);
    }
  }
}
Abigail outo(){
  printf("%.2lf\n",f[1]);
}
int main(){
  into();
  work();
  outo();
  return 0;
}