zoukankan      html  css  js  c++  java
  • 【BZOJ3143】【HNOI2013】游走(期望dp,高斯消元)

    首先想到怎么求出每一条边 i i i 在每次游走中被经过次数的期望 f i f_i fi,那么答案就可以贪心地取。(即让最大的 f i f_i fi 权值设为 1 1 1,次大的设为 2 2 2,……,最小的设为 m m m

    但是发现不好统计,或者时间无法承受。

    发现点数很小( n ≤ 500 nleq 500 n500),于是想到怎么通过点的期望值转移到边上。

    容易得到:如果设每一个点 u u u 在每次游走中被经过次数的期望 g u g_u gu,每个点的度数为 d e g u deg_u degu,然后假设某一条边 i i i ( u , v ) (u,v) (u,v),那么有 f i = g u d e g u + g v d e g v f_i=dfrac{g_u}{deg_u}+dfrac{g_v}{deg_v} fi=degugu+degvgv

    所以我们只需要求出 g g g 就可以得到 f f f 了。

    容易得到状态转移方程:

    g u = { 1 + ∑ ( u , v ) g v d e g v u = 1 ∑ ( u , v ) g v d e g v 1 < u < n g_u= egin{cases} 1+sumlimits_{(u,v)}dfrac{g_v}{deg_v}&u=1\ sumlimits_{(u,v)}dfrac{g_v}{deg_v}&1<u<n\ end{cases} gu=1+(u,v)degvgv(u,v)degvgvu=11<u<n

    解释一下:

    首先为什么 u = 1 u=1 u=1 的时候要比其他时候多加 1 1 1,因为初始化的时候本来就应该是 g 1 = 1 g_1=1 g1=1

    然后为什么 g n g_n gn 不考虑,因为到了 n n n 之后就结束游走了,不可能再走向下一条边。

    然后这个状态转移方程就可以看成是有 n − 1 n-1 n1 个未知数, n − 1 n-1 n1 条式子的方程,用高斯消元做就好了。

    最后代码如下:

    #include<bits/stdc++.h>
    
    #define N 510
    #define M 125010
    
    using namespace std;
    
    int n,m;
    int deg[N],from[M],to[M];
    double a[N][N],x[N],f[M];
    
    vector<int>e[N];
    
    void Gauss()
    {
    	for(int i=1;i<n;i++)
    	{
    		int p=i;
    		for(int j=i+1;j<n;j++)
    			if(fabs(a[j][i])>fabs(a[p][i])) p=j;
    		if(i!=p) swap(a[i],a[p]);
    		for(int j=i+1;j<n;j++)
    		{
    			double tmp=a[j][i]/a[i][i];
    			for(int k=i;k<=n;k++) a[j][k]-=a[i][k]*tmp;
    		}
    	}
    	for(int i=n-1;i>=1;i--)
    	{
    		for(int j=i+1;j<n;j++) a[i][n]-=x[j]*a[i][j];
    		x[i]=a[i][n]/a[i][i];
    	}
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%d%d",&from[i],&to[i]);
    		e[from[i]].push_back(to[i]);
    		e[to[i]].push_back(from[i]);
    		deg[from[i]]++,deg[to[i]]++;
    	}
    	for(int u=1;u<n;u++)
    	{
    		a[u][u]=1;
    		for(int i=0,size=e[u].size();i<size;i++)
    		{
    			int v=e[u][i];
    			if(v!=n) a[u][v]-=1.0/deg[v];
    		}
    	}
    	a[1][n]=1;
    	Gauss();
    	for(int i=1;i<=m;i++)
    	{
    		int u=from[i],v=to[i];
    		if(u!=n) f[i]+=x[u]/deg[u];
    		if(v!=n) f[i]+=x[v]/deg[v];
    	}
    	sort(f+1,f+m+1);
    	double ans=0;
    	for(int i=1;i<=m;i++)
    		ans+=i*f[m-i+1];
    	printf("%.3lf
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Java 简单算法--打印乘法口诀(只使用一次循环)
    Java简单算法--求100以内素数
    ubuntu 16.04 chrome flash player 过期
    java 网络API访问 web 站点
    java scoket (UDP通信模型)简易聊天室
    leetcode1105 Filling Bookcase Shelves
    leetcode1140 Stone Game II
    leetcode1186 Maximum Subarray Sum with One Deletion
    leetcode31 Next Permutation
    leetcode834 Sum of Distances in Tree
  • 原文地址:https://www.cnblogs.com/ez-lcw/p/14448651.html
Copyright © 2011-2022 走看看