原题题址
说说实话,这题在没接触过概率dp的时候我不太会做,自学了点概率dp的时候就不太难了。说实话我的题意分析有问题,以为只需要算出期望日期就行了,结果忘了还要算期望花费。
其实两次期望dp就行,而且由于两个转移方式相同,推出一个就行了。
时间期望,
d
p
1
[
i
]
dp_{1}[i]
dp1[i]表示到点i的期望时间,根据每个点的概率回推,每次回推时间多1,很容易得到。
d
p
1
[
i
]
=
d
p
1
[
i
]
N
+
∑
v
d
p
1
[
v
]
N
+
1
dp_{1}[i] = \frac{dp_{1}[i]}{N}+\frac{\sum_{v}dp_{1}[v]}{N}+1
dp1[i]=Ndp1[i]+N∑vdp1[v]+1
根据类推,每次回推的增加的部分改成每个点期望花费的价值(即是时间)
d
p
2
[
i
]
=
d
p
2
[
i
]
N
+
∑
v
d
p
2
[
v
]
N
+
d
p
1
[
i
]
dp_{2}[i] = \frac{dp_{2}[i]}{N}+\frac{\sum_{v}dp_{2}[v]}{N}+dp_{1}[i]
dp2[i]=Ndp2[i]+N∑vdp2[v]+dp1[i]
直接上代码:
#include"iostream"
#include"cstdio"
#include"vector"
using namespace std;
const int maxn = 2e5+10;
vector<int> a[maxn];
double dp1[maxn],dp2[maxn];
int P,E;
double Find1(int x){
if(dp1[x]>0||x == P)return dp1[x];
else{
int L = a[x].size();
double res = 0;
for(int i = 0;i < L;i++)
res+=((Find1(a[x][i])));
dp1[x] = (res+L+1)/(L);
}
return dp1[x];
}
double Find2(int x){
if(dp2[x]>0||x == P)return dp2[x];
else{
int L = a[x].size();
double res = 0;
for(int i = 0;i < L;i++)
res+=((Find2(a[x][i])));
dp2[x] = (res + dp1[x])/(L)+dp1[x];
}
return dp2[x];
}
int main(){
int T;
cin >> T;
while(T--){
scanf("%d%d",&P,&E);
for(int i = 0;i <= P;i++)
{
dp1[i] = 0;
dp2[i] = 0;
a[i].clear();
}
for(int i = 0;i < E;i++)
{
int u,v;
scanf("%d%d",&u,&v);
a[u].push_back(v);
}
Find1(1);
double ans = Find2(1);
//cout <<ans;
printf("%.2lf\n",ans);
}
return 0;
}