codeforces 113D&bzoj 3270

14 篇文章 0 订阅
1 篇文章 0 订阅

题目大意:

一张图,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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值