1. 程式人生 > >【bzoj3143】[Hnoi2013]遊走 期望dp+高斯消元

【bzoj3143】[Hnoi2013]遊走 期望dp+高斯消元

接下來 map 頂點 log ++ double ans fabs limits

題目描述

一個無向連通圖,頂點從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位小數。

樣例輸入

3 3
2 3
1 2
1 3

樣例輸出

3.333


題解

期望dp+高斯消元

顯然讓期望經過次數越大的邊編號越小即可,所以只要求出邊的期望經過次數就能出解。

由於邊數過大,但是邊的期望經過次數可以用它連接的兩個點的 期望次數/度數 之和得到,所以只要求出點的期望經過次數就能出解。

對於每個一般的點y,它的期望經過次數為$f[y]=\sum\limits_{exist\ x\to y}\frac{f[x]}{d[x]}$。

然後有兩個特殊的點:1和n,由於1是起點,所以要算上初始經過的一次,在表達式的右邊加1;由於n是終點,到達終點就不能夠再經過其它邊,所以按照上面的期望經過次數的計算方式,n的期望經過次數應該強制設置為0

。(並非部分題解中的1)(所以其實上面的期望經過次數指的是“到達終點前的期望經過次數”,實際上也應該這樣理解)

由於圖是沒有dp順序的,所以需要使用高斯消元求出f[i]。

求出所有點的期望經過次數,然後計算出邊的經過次數,排序出解。

#include <cstdio>
#include <cmath>
#include <algorithm>
#define N 510
using namespace std;
struct data
{
	int x , y;
	double p;
}e[200010];
double a[N][N];
int n , map[N][N] , d[N];
bool cmp(data a , data b)
{
	return a.p > b.p;
}
void gauss()
{
	int i , j , k;
	double t;
	for(i = 1 ; i <= n ; i ++ )
	{
		for(k = i , j = i + 1 ; j <= n ; j ++ )
			if(fabs(a[k][i]) < fabs(a[j][i]))
				k = j;
		for(j = 1 ; j <= n + 1 ; j ++ ) swap(a[i][j] , a[k][j]);
		for(j = i + 1 ; j <= n ; j ++ )
		{
			t = a[j][i] / a[i][i];
			for(k = i ; k <= n + 1 ; k ++ ) a[j][k] -= a[i][k] * t;
		}
	}
	for(i = n ; i >= 1 ; i -- )
	{
		for(j = n ; j > i ; j -- ) a[i][n + 1] -= a[j][n + 1] * a[i][j];
		a[i][n + 1] /= a[i][i];
	}
}
int main()
{
	int m , i , j;
	double ans = 0;
	scanf("%d%d" , &n , &m);
	for(i = 1 ; i <= m ; i ++ ) scanf("%d%d" , &e[i].x , &e[i].y) , map[e[i].x][e[i].y] = map[e[i].y][e[i].x] = 1 , d[e[i].x] ++ , d[e[i].y] ++ ;
	for(i = 1 ; i < n ; i ++ )
	{
		a[i][i] = 1;
		for(j = 1 ; j <= n ; j ++ ) if(map[j][i]) a[i][j] = -1.0 / d[j];
	}
	a[1][n + 1] = a[n][n] = 1;
	gauss();
	for(i = 1 ; i <= m ; i ++ ) e[i].p = a[e[i].x][n + 1] / d[e[i].x] + a[e[i].y][n + 1] / d[e[i].y];
	sort(e + 1 , e + m + 1 , cmp);
	for(i = 1 ; i <= m ; i ++ ) ans += e[i].p * i;
	printf("%.3lf\n" , ans);
	return 0;
}

【bzoj3143】[Hnoi2013]遊走 期望dp+高斯消元