原題目
問題描述
假設你在玩一個很無聊的迷宮遊戲,在一個很大的迷宮裏面(迷宮可以用一個無向圖來表示),你隨機出生在一個節點,你的目的是走到迷宮唯一的出口,每一時刻你可以選擇從當前節點走到一個相鄰的節點,也可以考慮隨機傳送到迷宮的某一地點。
你想要知道,在最優的策略下,平均需要多長時間能到達出口。輸入格式
第一行,圖的節點數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號點)的平均時間。
解答
設對於點
然後我們考慮什麼時候我們會用到傳送。
很容易得知,如果對於起點
所以我們的任務就變成了確定最小的
那麼傳送的期望目標就是所有
結合樣例1,我們可以得到將所有最佳策略不傳送的點求均值
所以我們只需要在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;
}