原题目
问题描述
假设你在玩一个很无聊的迷宫游戏,在一个很大的迷宫里面(迷宫可以用一个无向图来表示),你随机出生在一个节点,你的目的是走到迷宫唯一的出口,每一时刻你可以选择从当前节点走到一个相邻的节点,也可以考虑随机传送到迷宫的某一地点。
你想要知道,在最优的策略下,平均需要多长时间能到达出口。输入格式
第一行,图的节点数N和图的边数M
节点编号从1到N,其中1号节点为迷宫出口。输出格式
一个实数,表示平均到达出口的时间,保留两位小数。 样例数据:(以下包含3个样例,在实际测试时会在不同的文件内,这里简单起见用分隔符分开)
样例输入
5 4
1 2
2 3
3 4
4 5样例输出
1.67
样例输入
5 0
样例输出
4.00
样例输入
5 4
1 2
1 3
1 4
1 5样例输出
0.80
样例说明
l 对于第一个图,最优策略如下:若初始时在1, ,2, 3号节点,则可直接走到出口,所耗费的时间分别为0,1,2,若初始时在4,
5号节点,则需要使用随机传送,直到传送至1,2,3号节点之一,传送所耗费的期望时间是5/3,传送结束后距离出口的平均距离是1,因此在4,5号节点的最少平均耗费时间是8/3,于是所有节点的平均期望耗费时间是5/3
l 对于第二个图,由于各个节点之间没有边相连,因此唯一可以到达出口的方式就是随机传送,对于2,3,4,5号节点,传送到1号节点的概率是1/5,因此可以计算出它们期望使用的传送次数为5次,所以总的平均期望耗费时间为4(因为初始在1号节点时不需耗费时间)。
l 对于第三个图,所有节点的最优策略就是直接走到出口,总的平均耗费时间是0.8。数据规模和约定
对于30%的数据,2<=N<=100, M <= 100 对于100%的数据,2<=N<=100000, M <= 100000
提示
输入数据规模较大。
输入数据中有可能有重边或自环存在。
题目大意
给定一个无向图,穿过每条边所需的时间均为1,也可以花费1的时间随机传送到图上的一个位置。你随机出生在图上的一个点上,到达出口(1号点)的平均时间。
解答
设对于点
i
,不使用传送离开的时间为
然后我们考虑什么时候我们会用到传送。
很容易得知,如果对于起点
j
,最佳策略中使用了传送,则对于所有的
所以我们的任务就变成了确定最小的
j
。
那么传送的期望目标就是所有
结合样例1,我们可以得到将所有最佳策略不传送的点求均值
ave
,并计数
cnt
,则此时传送的期望时间为
ncnt
。若
dis(j)>ave+ncnt
,此时,使用传送的期望时间就要比不传送的期望低。
所以我们只需要在bfs过程中判断当前点是否需要传送,如果需要,就退出bfs即可。
参考代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <iostream>
using namespace std;
class Edge {
public:
int to;
int next;
};
int dis[100500];
Edge es[200500];
int head[100500];
int et = -1;
int cnt = 1, sum = 0;
int n, m;
void addEdge(int u, int v) {
et++;
es[et].to = v;
es[et].next = head[u];
head[u] = et;
et++;
es[et].to = u;
es[et].next = head[v];
head[v] = et;
}
void bfs(int st) {
memset(dis, -1, sizeof(dis));
dis[st] = 0;
queue<int> ser;
ser.push(st);
int rt;
while (!ser.empty()) {
rt = ser.front();
if ((double)dis[rt]+1 > (sum + n) / (double)cnt)
break;
for (int i = head[rt]; i != -1; i = es[i].next)
if (dis[es[i].to] == -1) {
dis[es[i].to] = dis[rt] + 1;
cnt++;
sum += dis[es[i].to];
ser.push(es[i].to);
}
ser.pop();
}
}
void readin() {
scanf("%d %d", &n, &m);
int x, y;
memset(head, -1, sizeof(head));
for (int i = 0; i < m; i++) {
scanf("%d %d", &x, &y);
addEdge(x, y);
}
}
void getAns() {
double ave = (sum + n) / (double)cnt;
double ans = ave * (double)(n - cnt) + (double)sum;
ans /= (double)n;
printf("%.2lf", ans);
}
int main() {
freopen("maze.in", "r", stdin);
freopen("maze.out", "w", stdout);
readin();
bfs(1);
getAns();
return 0;
}