1. 程式人生 > >[HNOI2013][BZOJ3143] 遊走 - 高斯消元

[HNOI2013][BZOJ3143] 遊走 - 高斯消元

pac rom pre -- 相等 輸入輸出格式 ios 數據 3.3

題目描述

一個無向連通圖,頂點從1編號到N,邊從1編號到M。 小Z在該圖上進行隨機遊走,初始時小Z在1號頂點,每一步小Z以相等的概率隨機選 擇當前頂點的某條邊,沿著這條邊走到下一個頂點,獲得等於這條邊的編號的分數。當小Z 到達N號頂點時遊走結束,總分為所有獲得的分數之和。 現在,請你對這M條邊進行編號,使得小Z獲得的總分的期望值最小。

輸入輸出格式

輸入格式:

第一行是正整數N和M,分別表示該圖的頂點數 和邊數,接下來M行每行是整數u,v(1<=u,v<=N),表示頂點u與頂點v之間存在一條邊。 輸入保證30%的數據滿足N<=10,100%的數據滿足2<=N<=500且是一個無向簡單連通圖。

輸出格式:

僅包含一個實數,表示最小的期望值,保留3位小數。

輸入輸出樣例

輸入樣例#1:
3 3
2 3
1 2
1 3
輸出樣例#1:
3.333

說明

邊(1,2)編號為1,邊(1,3)編號2,邊(2,3)編號為3。


題解

  期望dp。

  貪心地想,我們肯定要往那個期望到達次數最大的邊賦最小的權值;

  所以問題轉化成了求邊的期望到達次數;

  我們發現一條邊連著唯一的兩個點,我們要知道邊的期望,首先要知道到達每個點的期望次數;

  我們設f[i]表示第i個點的期望到達次數,即f[i] = ∑(f[to[i]] * deg[to[i]]) ,deg[i]表示一個點的度數;

  這樣我們發現可以高斯消元解出;要註意的是1號點的期望還得加上1因為從他開始必定經過;

  然後求g[i],即邊i的期望到達次數,g[i] = f[l[i]]/deg[l[i]] + f[r[i]]/deg[r[i]],l r表示這個邊鏈接的兩個點;

  要註意如果是n號點的話,就不用考慮,因為到了n點就不會繼續遊走了;

  然後就貪心地賦邊權;


Code

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using
namespace std; #define eps 1e-8 int n, m; struct edge { int from, to; int nxt; }ed[500010]; int deg[505], head[505]; int cnt; int fr[500010], tt[500010]; inline void add(int x, int y){ed[++cnt] = (edge){x, y, head[x]};head[x] = cnt;} double g[250010]; double a[505][505]; double ans; inline void Gauss_() { for (register int i = 1 ; i < n ; i ++) { int pivot = i ; for (register int j = i + 1 ; j < n ; j ++) { if (fabs(a[j][i] - a[pivot][i]) <= eps) pivot = j; } if (pivot != i) for (register int j = 1 ; j <= n ; j ++) swap(a[i][j], a[pivot][j]); for (register int j = n ; j >= i ; j --) a[i][j] /= a[i][i]; for (register int j = 1 ; j < n ; j ++) if (i != j) for (register int k = n ; k >= i ; k --) a[j][k] -= a[j][i] * a[i][k]; } } int main() { scanf("%d%d", &n, &m); for (register int i = 1; i <= m; i ++) { int x, y; scanf("%d%d", &x, &y); deg[x]++, deg[y]++; fr[i] = x, tt[i] = y; add(x, y); add(y, x); } a[1][n] = 1; for (register int i = 1; i < n; i ++) { a[i][i] = 1; for (register int j = head[i]; j; j = ed[j].nxt) { int to = ed[j].to ; if (to != n) a[i][to] = -1.0/deg[to]; } } Gauss_(); for (register int i = 1 ; i <= m ; i ++) { if (fr[i] != n ) g[i] += a[fr[i]][n] * (1.0 / deg[fr[i]]) ; if (tt[i] != n) g[i] += a[tt[i]][n] * (1.0 / deg[tt[i]]); } sort(g + 1, g + 1 + m); for (register int i = 1 ; i <= m ; i ++) ans += (m - i + 1) * 1.0 * g[i]; printf("%.3lf", ans); return 0; }

[HNOI2013][BZOJ3143] 遊走 - 高斯消元