【2019 南京网络赛】Robots

1.做这个题目之前,首先可以做一下 绿豆蛙的归宿 这道题目。二者几乎一样。其实二者的核心思想都是倒着推期望,为什么会倒着推?其实也有很多种解释,有句话说得很好,其实就是诠释了为什么期望要倒着DP。终点是概率的结束,是期望的开始。

2.绿豆蛙的归宿:f(x)  代表从x到n路径权值的期望。f(n)=0,从DAG的角度来看,终点的可以逆着拓扑序向上。首先可以计算所有与终点相连的边的贡献,并且把贡献保存到这些点中。这样逆着拓扑序就可以回到起点,所有的贡献都会被累加到起点。也正是因为DAG是无环的,所以所有贡献是可以做简单的叠加,不会有重复计算。所以,只要找到了当前节点和前驱节点的累加关系,逆着拓扑序把这个图给遍历一遍就是答案。分析到了这里,那么这个累加的式子就是显然的了。

                                                                  f(u)=\sum{(f[v]+w_{u-v})}/deg[v]

解释一下就是当前节点的值由所有和它相连的节点的值贡献而来,贡献值等于这个节点本来具有的值和他们之间的边权之和再乘一下贡献的权重就是答案。写法也很简单,因为是倒着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[u]=\frac{\sum dp[v]}{out[u]+1}+\frac{dp[u]}{out[u]+1}+1

化简一下:

                                    dp[u]=\frac{\sum dp[v]}{out[u]}+\frac{1}{out[u]}+1

有了天数,就是有了费用,然后再DP一遍,dpE[x]代表从x走到n的费用的期望:

                                    dpE[u]=\frac{\sum dpE[v]}{out[u]+1}+\frac{dpE[u]}{out[u]+1}+dp[u]

化简一下

                                  dpE[u]=\frac{\sum dpE[v]}{out[u]}+\frac{out[u]+1}{out[u]}*dp[u]

然后这个题就写完了,可以直接和刚才一样,反着建图,一边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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值