题意:
给定一个无向连通图,有两个人分别在(a),(b)点,每个点有概率留在本地不动,剩下等概率移动到一个相邻的点。问他们在每个点相遇的概率。
其中,(nleq 20)。
知识点:
期望概率DP,高斯消元
解法:
首先我想讲一下为什么我想写这一篇题解,因为网上大多数其他的题解里面描述的都有一点很关键的问题,导致很多人看题解学习的时候都被误导了(包括我)。感谢wjgg教会了我这道题正确的理解方法。
其实错在哪里了呢?是因为他们把设的未知数设成了概率。其实大家只要仔细想一想,为什么起始状态的概率会(>1)呢?没有那种概率是可以大于1的啊!想到这里相信大家都已经知道了设概率其实是不对的了。
设(f_{u,v})表示甲走到(u)号点,乙走到(v)号点的期望次数(这里的期望可以理解为有无穷种路线,其中达到这种状态的平均次数),(p_i)是留在本地的概率,(out_i)是从某点出发的概率(这个(out_i)很好算,就用不留在本地的概率除以当前点的度数就可以了)。
可以列出状态转移方程:
其中必须确保上述方程中的右边满足(i ot=j,i ot=u,i ot=v,j ot=u,j ot=v,u ot=v)。因为只要到了一个(u=v)的点就碰面了,也就结束游走了。
特别地,初始所在的(a),(b)点右边期望要(+1),表示刚开始所在的地方必定经过一次。
然后高斯消元即可求出答案。
备注:
-
枚举(u)和(v)的入点的时候,(id)的顺序不可以反,必须按照从(u)到(v)(或者是(i)到(j))这样的顺序,否则表示的就不是当前那个未知数。此处高斯消元的第(i)行理解为第(i)个方程。
-
为什么不能设概率而要设期望,是因为概率的和为(1)表示不出来,无法去重,所以列不出正确的方程。
-
为什么最终的答案概率(=)期望,是因为这些点只会经过(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;
}