【期望·高斯消元】HNOI2013 遊走

題目傳送門

容易想到,應該是算出經過每一條邊的期望,然後給期望大的賦小的編號,期望小的賦大的編號。
沒有其它奇奇怪怪的附加屬性,只是隨意地走的話,經過邊的期望應該只是和圖的長相有關聯,也就是隻和邊兩邊的結點有關,而且邊的數量沒有限制,最大可以達到n2n^2的級別,所以我們可以用點的期望來算邊的期望。
如果知道了點的期望,那麼邊的期望就是
1點的期望*\frac{1}{這個點的邊數}(這條邊兩個端點這麼算的和)
那麼就要開始求點的期望了
對於每一個點xx,設點的期望是f[x]f[x],與xx相鄰的有num[x]num[x]條邊,相鄰的點是1k1至k,則有下式:

每一個點都可以列出這樣的式子,進行高斯消元就可以解出f[]f[]
特殊地,要注意第一個點和第nn個點
"遊走"是從點11開始,則計算點11期望時實際期望應該是原期望+11
到了點nn不會繼續"遊走"了 則若有點和nn相連,那麼在計算期望時是不需要將其算入的


#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
#define N 505
int n,m;
double f[N][N],ans[N],q[N*N];
vector<int>G[N];
int F[N*N],T[N*N];
double eps=1e-7;
int rd()
{
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return f*x;
}
double Abs(double x)
{
	if(x>0) return x;
	return -x;
}
void Gauss()
{
	for(int i=1;i<n;i++)
	{
		int r=i;
		for(int j=i+1;j<n;j++)
			if(Abs(f[r][i])<Abs(f[j][i]))
				r=j;
		//找這一列係數最大的那一行 
		//每次丟掉的那個都挪到了上面去 所以從i開始就可以 
		if(i!=r) swap(f[i],f[r]);
		double div=f[i][i];//現在第i行是目標 
		for(int j=i;j<=n;j++)
			f[i][j]/=div;//第j個係數化爲1
		for(int j=i+1;j<=n;j++)
		{
			div=f[j][i];
			for(int k=i;k<=n;k++)
				f[j][k]-=f[i][k]*div;
		}
	}
	for(int i=n-1;i>=1;i--)
	{//迴帶 
		ans[i]=f[i][n];
		for(int j=i+1;j<n;j++)
			ans[i]-=(f[i][j]*ans[j]);
		//ans[i]/=f[i][i];
	}
	return ;
}
bool cmp(double a,double b)
{
	return a>b;
}
int main()
{
	n=rd(),m=rd();
	for(int i=1;i<=m;i++)
	{
		int u=rd(),v=rd();
		G[u].push_back(v);
		G[v].push_back(u);
		F[i]=u,T[i]=v;
	}
	f[1][n]=1.0;//第1個點一開始就在 期望是1 
	for(int i=1;i<n;i++)
	{
		f[i][i]=1.0;
		for(int j=0;j<G[i].size();j++)
			if(G[i][j]!=n)
				f[i][G[i][j]]=-1.0/G[G[i][j]].size();
	}
	Gauss();
	for(int i=1;i<=m;i++)
		q[i]=ans[F[i]]/G[F[i]].size()+ans[T[i]]/G[T[i]].size();
	sort(q+1,q+m+1,cmp);
	double res=0;
	for(int i=1;i<=m;i++)
		res+=q[i]*(1.0*i);
	printf("%.3lf\n",res);
}
發佈了164 篇原創文章 · 獲贊 79 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章