zoukankan      html  css  js  c++  java
  • BZOJ3270 博物馆

    题意:

    给定一个无向连通图,有两个人分别在(a)(b)点,每个点有概率留在本地不动,剩下等概率移动到一个相邻的点。问他们在每个点相遇的概率。
    其中,(nleq 20)

    知识点:

    期望概率DP,高斯消元

    解法:

    首先我想讲一下为什么我想写这一篇题解,因为网上大多数其他的题解里面描述的都有一点很关键的问题,导致很多人看题解学习的时候都被误导了(包括我)。感谢wjgg教会了我这道题正确的理解方法。

    其实错在哪里了呢?是因为他们把设的未知数设成了概率。其实大家只要仔细想一想,为什么起始状态的概率会(>1)呢?没有那种概率是可以大于1的啊!想到这里相信大家都已经知道了设概率其实是不对的了。

    (f_{u,v})表示甲走到(u)号点,乙走到(v)号点的期望次数(这里的期望可以理解为有无穷种路线,其中达到这种状态的平均次数),(p_i)是留在本地的概率,(out_i)是从某点出发的概率(这个(out_i)很好算,就用不留在本地的概率除以当前点的度数就可以了)。

    可以列出状态转移方程:

    [f_{u,v}=p_u imes p_v imes f_{u,v}+displaystylesum_{e(i,u)} out_i imes p_v imes f_{i,v} +displaystylesum_{e(j,v)} out_j imes p_u imes f_{u,j}+displaystylesum_{e(i,u)}displaystylesum_{e(j,v)}out_i imes out_j imes f_{i,j} ]

    其中必须确保上述方程中的右边满足(i ot=j,i ot=u,i ot=v,j ot=u,j ot=v,u ot=v)。因为只要到了一个(u=v)的点就碰面了,也就结束游走了。

    特别地,初始所在的(a)(b)点右边期望要(+1),表示刚开始所在的地方必定经过一次。

    然后高斯消元即可求出答案。

    备注:

    1. 枚举(u)(v)的入点的时候,(id)的顺序不可以反,必须按照从(u)(v)(或者是(i)(j))这样的顺序,否则表示的就不是当前那个未知数。此处高斯消元的第(i)行理解为第(i)个方程。

    2. 为什么不能设概率而要设期望,是因为概率的和为(1)表示不出来,无法去重,所以列不出正确的方程。

    3. 为什么最终的答案概率(=)期望,是因为这些点只会经过(1)次,所以概率(=)期望。

    代码:

    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int maxn=410;
    const double eps=1e-8;
    int n,m,A,B,d[maxn],id[21][21],head[21],tot,num;
    double a[maxn][maxn],p[21],out[21];
    struct node
    {
    	int nxt,to;
    }edge[maxn*maxn];
    
    void add(int u,int v)
    {
    	edge[++tot]=(node){head[u],v};
    	head[u]=tot;
    }
    
    void gauss(int N)
    {
    	int i,j,k,l,p;
    	double tt;
    	for (i=1,j=1;i<=N&&j<=N;i++,j++)
    	{
    		p=i;
    		for (k=i+1;k<=N;k++)
    			if (fabs(a[k][j])>fabs(a[p][j]))
    				p=k;
    		if (p!=i)
    		{
    			for (k=j;k<=N+1;k++)
    				swap(a[i][k],a[p][k]);
    		}
    		if (fabs(a[i][j])<eps)
    		{
    			i--;
    			continue;
    		}
    		tt=a[i][j];
    		for (k=j;k<=N+1;k++)
    			a[i][k]/=tt;
    		for (k=i+1;k<=N;k++)
    		{
    			tt=a[k][j];
    			for (l=j;l<=N+1;l++)
    				a[k][l]-=tt*a[i][l];
    		}
    	}
    	for (i=N;i>=1;i--)
    		for (j=i+1;j<=N;j++)
    			a[i][N+1]-=a[i][j]*a[j][N+1];
    }
    
    int main()
    {
    	int i,j,k,l,u,v;
    	scanf("%d%d%d%d",&n,&m,&A,&B);
    	for (i=1;i<=n;i++)
    		for (j=1;j<=n;j++)
    			id[i][j]=++num;
    	for (i=1;i<=m;i++)
    	{
    		scanf("%d%d",&u,&v);
    		d[u]++,d[v]++;
    		add(u,v),add(v,u);
    	}
    	for (i=1;i<=n;i++)
    	{
    		scanf("%lf",&p[i]);
    		out[i]=(1-p[i])/d[i];
    	}
    	for (i=1;i<=n;i++)
    		for (j=1;j<=n;j++)
    		{
    			a[id[i][j]][id[i][j]]=-1;
    			if (i!=j)
    				a[id[i][j]][id[i][j]]+=p[i]*p[j];
    			for (k=head[i];k;k=edge[k].nxt)
    			{
    				u=edge[k].to;
    				if (u!=i&&u!=j)
    					a[id[i][j]][id[u][j]]+=out[u]*p[j];
    			}
    			for (k=head[j];k;k=edge[k].nxt)
    			{
    				v=edge[k].to;
    				if (v!=i&&v!=j)
    					a[id[i][j]][id[i][v]]+=out[v]*p[i];
    			}
    			for (k=head[i];k;k=edge[k].nxt)
    			{
    				u=edge[k].to;
    				for (l=head[j];l;l=edge[l].nxt)
    				{
    					v=edge[l].to;
    					if (u!=v)
    						a[id[i][j]][id[u][v]]+=out[u]*out[v];
    				}
    			}
    		}
    	a[id[A][B]][num+1]=-1;
    	gauss(num);
    	for (i=1;i<=n;i++)
    		printf("%0.6lf ",a[id[i][i]][num+1]);
    	puts("");
    	return 0;
    }
    
  • 相关阅读:
    常见的HTTP状态码有哪些?
    使用Hbuild打包APP
    Android APK反编译
    小程序|页面跳转的方法
    vi/vim 命令
    webpack学习笔记
    egg框架学习笔记
    IOS弹出系统键盘后,页面不恢复
    js上传文件
    webpack学习笔记
  • 原文地址:https://www.cnblogs.com/Ronald-MOK1426/p/12611679.html
Copyright © 2011-2022 走看看