2013BJOI Day1 第一题 迷宫

原题目

问题描述

  假设你在玩一个很无聊的迷宫游戏,在一个很大的迷宫里面(迷宫可以用一个无向图来表示),你随机出生在一个节点,你的目的是走到迷宫唯一的出口,每一时刻你可以选择从当前节点走到一个相邻的节点,也可以考虑随机传送到迷宫的某一地点。
  你想要知道,在最优的策略下,平均需要多长时间能到达出口。

输入格式

第一行,图的节点数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 ,不使用传送离开的时间为dis(i),这里我们可以用bfs得到所有的 dis
然后我们考虑什么时候我们会用到传送。
很容易得知,如果对于起点 j ,最佳策略中使用了传送,则对于所有的k(dis(k)>dis(j))在最佳策略中必然都使用了传送。
所以我们的任务就变成了确定最小的 j
那么传送的期望目标就是所有dis<dis(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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值