1.做这个题目之前,首先可以做一下 绿豆蛙的归宿 这道题目。二者几乎一样。其实二者的核心思想都是倒着推期望,为什么会倒着推?其实也有很多种解释,有句话说得很好,其实就是诠释了为什么期望要倒着DP。终点是概率的结束,是期望的开始。
2.绿豆蛙的归宿: 代表从x到n路径权值的期望。f(n)=0,从DAG的角度来看,终点的可以逆着拓扑序向上。首先可以计算所有与终点相连的边的贡献,并且把贡献保存到这些点中。这样逆着拓扑序就可以回到起点,所有的贡献都会被累加到起点。也正是因为DAG是无环的,所以所有贡献是可以做简单的叠加,不会有重复计算。所以,只要找到了当前节点和前驱节点的累加关系,逆着拓扑序把这个图给遍历一遍就是答案。分析到了这里,那么这个累加的式子就是显然的了。
解释一下就是当前节点的值由所有和它相连的节点的值贡献而来,贡献值等于这个节点本来具有的值和他们之间的边权之和再乘一下贡献的权重就是答案。写法也很简单,因为是倒着DP,所以把图反着建一下然后一边TopSort,一边DP。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 10;
int head[MAXN], tot;
struct e{
int y,next,v;
}edge[MAXN << 1];
inline void add(int x, int y, int v)
{
edge[++tot].y = y;
edge[tot].next = head[x];
head[x] = tot;
edge[tot].v = v;
}
int deg[MAXN], out[MAXN];
double dp[MAXN];//dp[i]代表从i点走到N点花费的期望
using namespace std;
queue<int>q;
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
deg[u]++, out[u]++;
add(v, u, w);
}
q.push(n);
while (q.size()) {
int x = q.front(); q.pop();
for (int i = head[x]; i; i = edge[i].next) {
int y = edge[i].y;
dp[y] += (dp[x] + edge[i].v) / deg[y];
out[y]--;
if (!out[y]) q.push(y);
}
}
printf("%.2lf\n", dp[1]);
}
(3)然后是Robots。依然是一个DAG,开始机器人在1点,然后每次都会等概率的停在原地或者走向和它连接的下一个点。每次走的花费是当前点已经花费的天数,问走到n的期望花费。还是求期望,首先是求一下天数的期望。dp[x]代表从x走到n的期望天数。和刚才几乎是一样的。
化简一下:
有了天数,就是有了费用,然后再DP一遍,dpE[x]代表从x走到n的费用的期望:
化简一下
然后这个题就写完了,可以直接和刚才一样,反着建图,一边TopSort一边DP,但是为了好理解,记忆化搜索一下也不错。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
vector<int>mp[N];
double dpD[N], dpE[N];//dpD表示天数的期望,dpE表示花费的期望
int out[N];
void init()
{
memset(dpE, 0, sizeof(dpE));
memset(dpD, 0, sizeof(dpD));
memset(out, 0, sizeof(out));
for (int i = 0; i < N; i++)mp[i].clear();
}
// dpD[u]=sigma(dpD[v])/out[u]+1+1/out[u]
double dfsD(int u)
{
if (dpD[u] != 0 || out[u] == 0)return dpD[u];
double sum = 0;
for (int i = 0; i < mp[u].size(); i++)
{
int to = mp[u][i];
dfsD(to);
sum += dpD[to];
}
dpD[u] = sum /out[u]*1.0 + 1 + 1.0/ out[u];
return dpD[u];
}
// dpE[u]=sigma(dpE[v])/out[u]+(out[u]+1)/out[u]*dp[u]
double dfsE(int u)
{
if (dpE[u] != 0 || out[u] == 0)return dpE[u];
double sum = 0;
for (int i = 0; i < mp[u].size(); i++)
{
int to = mp[u][i];
dfsE(to);
sum += dpE[to];
}
dpE[u] = sum/ out[u]*1.0 + (out[u] + 1)*1.0/out[u]*1.0 * dpD[u];
return dpE[u];
}
int main()
{
int T; scanf("%d", &T);
while (T--)
{
init();
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++){
int u, v1; scanf("%d%d", &u, &v1);
out[u]++;
mp[u].push_back(v1);
}
dfsD(1);
dfsE(1);
printf("%.2lf\n", dpE[1]);
}
return 0;
}