题目大意:
一张图,N(N<=22)个点M条无向边,有两个人分别在A点B点。
对于每个点,有Pi的几率留在原地,否则等概率的向相连的点走去。
问对于每个点,有多少的概率两个人在这个点相遇?
这题显然是概率型dp+高斯消元。但是要注意求方程组的方法。
一开始,我试图枚举x,用P[x][x]表示同时在x点相遇的概率,P[i][j]表示从i,j出发同时到达x点的概率。
然而这样似乎可以做,但是解N次N*N个点的方程组,复杂度是O(N^7),妥妥超时。(不过听说卡常能卡过去?雾)
然后发现完全没必要这样搞:
因为这个方程组是N*N元的,我们直接用F[id[i][j]]表示从A,B到达i,j的概率就好了=。=(我是撒币吗)
那么显然我们都得到每对i,j之间相互转化的概率,并建立N*N元方程组,直接高斯消元。
有些细节要注意下,参考了黄学长的博客(http://hzwer.com/),才想起来初始状态的概率值是要加一的。
附代码
#include<bits/stdc++.h>
using namespace std;
double a[600][600],p[25],du[25];
int id[25][25],n,m,x,y,tot,st;
int beg[600],to[600],nex[600],len=0;
void add(int a,int b){beg[a]=++len;to[len]=b;nex[len]=beg[a];}
void build(int x,int y){
int bh=id[x][y];
a[bh][bh]=1.0;
for(int i=beg[x];i;i=nex[i])
for(int j=beg[y];j;j=nex[j]){
if(to[i]==to[j]) continue;
int tmp=id[to[i]][to[j]];
double l,r;
if(to[i]==x) l=p[to[i]];else l=(1-p[to[i]])/du[to[i]];
if(to[j]==y) r=p[to[j]];else r=(1-p[to[j]])/du[to[j]];
a[bh][tmp]-=l*r;
}
}
int main(){
memset(beg,0,sizeof(beg));
scanf("%d%d%d%d",&n,&m,&x,&y);
for(int i=0,l,r;i<m;i++){
scanf("%d%d",&l,&r);
add(l,r);add(r,l);
du[l]++;du[r]++;
}
for(int i=1;i<=n;i++) add(i,i);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
id[i][j]=++tot;
a[id[x][y]][tot+1]=1;
for(int i=1;i<=n;i++)
scanf("%lf",&p[i]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
build(i,j);
for(int i=1;i<=tot;i++){
int tmp=i;
while(!a[tmp][i]&&tmp<=tot) tmp++;
if(tmp>tot) continue;
for(int j=i;j<=tot+1;j++) swap(a[i][j],a[tmp][j]);
for(int j=1;j<=tot;j++)
if(j!=i){
double t=a[j][i]/a[i][i];
for(int k=1;k<=tot+1;k++)
a[j][k]-=t*a[i][k];
}
}
for(int i=1;i<=n;i++){
int t=id[i][i];
printf("%.6f ",a[t][tot+1]/a[t][t]);
}
printf("\n");
return 0;
}