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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章