题目链接:https://nanti.jisuanke.com/t/41301
题目大意:有一张有向无环图,你要从入度为0的点走到一个出度为0的点,保证这两种点各只有一个,你可以走到下一个点也可以保持原地不动,每过一天可以走一步,每天都要加上一个值就是已过的天数作为代价,求代价的期望,比如到达一个点需要3天,那么就是1+2+3
题目思路:随机游走模板题?做过类似题目应该这题不成问题
首先先求出天数的期望
dp就是通过已知的信息来推出未知的信息,那么有什么东西的期望是我们已经知晓的呢?bingo,最后一个点他的天数期望一定是0(都已经到达终点了!)
所以这题就需要倒着来反向建图。
设dp[i]表示从i点到n的期望时间,那么他一共有可以去的点的数量+1种可能情况(跑路或者停留在原地),他去的点由于是倒着推,已经知道结果,所以就能得到当前的结果,当前的结果就是走一步到达j(j表示能到的点)或者在原地,然后再下一点到达n,所以公式为
移项
然后化简可以得到
但是这道题不是简简单单求个期望天数,而是求代价,其实就是每走一步都加上当前的期望天数
公式为
然后按照刚才一样的方法化简以后,把dp[i]用刚才得到的式子代入再化简,就能得到
以下是代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,a,b) for(ll i=a;i<=b;i++)
#define per(i,a,b) for(ll i=a;i>=b;i--)
const int MAXN = 1e5+5;
int du[MAXN],du2[MAXN],du3[MAXN];
vector<int>g[MAXN];
double dp[MAXN],cost[MAXN];
queue<int>q;
int main(){
int t,n,m,u,v;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
while(!q.empty())q.pop();
memset(du,0,sizeof(du));
memset(dp,0,sizeof(dp));
memset(cost,0,sizeof(cost));
rep(i,1,n)g[i].clear();
rep(i,1,m){
scanf("%d%d",&u,&v);
g[v].push_back(u);
du[u]++;
}
int temp=0,temp2=0;
rep(i,1,n){
if(!du[i])temp2=i,q.push(i);
if(!g[i].size())temp=i;
du3[i]=du2[i]=du[i];
}
while(!q.empty()){
int u=q.front();
q.pop();
int len=g[u].size();
if(du3[u]){
dp[u]/=1.0*du3[u];
dp[u]+=(1.0*du3[u]+1)/du3[u];
}
rep(i,0,len-1){
int xx=g[u][i];
dp[xx]+=dp[u];
du[xx]--;
if(!du[xx])q.push(xx);
}
}
q.push(temp2);
while(!q.empty()){
int u=q.front();
q.pop();
int len=g[u].size();
if(du3[u]){
cost[u]/=1.0*du3[u];
cost[u]+=(1.0*du3[u]+1)*dp[u]/du3[u];
}
rep(i,0,len-1){
int xx=g[u][i];
cost[xx]+=cost[u];
du2[xx]--;
if(!du2[xx])q.push(xx);
}
}
printf("%.2lf\n",cost[temp]);
}
return 0;
}