【BZOJ3143】【HNOI2013】游走 && 【BZOJ3270】博物馆 【高斯消元+概率期望】

刚学完 高斯消元,我们来做几道题吧!

T1:【BZOJ3143】【HNOI2013】游走

Description

一个无向连通图,顶点从1编号到N,边从1编号到M。

小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小 Z 到达 N 号顶点时游走结束,总分为所有获得的分数之和。

现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。

Input

第一行是正整数N和M,分别表示该图的顶点数 和边数,接下来MM行每行是整数u,v(1≤u,v≤N)u,v(1≤u,v≤N),表示顶点u与顶点v之间存在一条边。

输入保证30%的数据满足N≤10,100%的数据满足2≤N≤500且是一个无向简单连通图。

Output

仅包含一个实数,表示最小的期望值,保留3位小数。

题解:

这明显是一道概率期望题嘛!

一条边经过的概率为 $E(u,v)={f_u \over deg_u}+{f_v \over deg_v}$ ,其中u,v为边的两个端点,fx 表示过x点的概率, degx 表示点 x的度数(出度、入度)。

一个点经过的概率为 $f_u=\sum_v f_v\times deg_v$ , v 为与 u 相连的一个节点。

知道了这些,我们还是不能用 dp 求解,因为它不符合决策单调性,即一个点走过去还能走回来,一个点的概率会互相影响,这我们就要列方程求解了。

$$a[i][j]= 1/deg[j]$$

我们这样列一个方程组,不过要注意的是 1号点它是必定经过的所以概率要加 1,n 的概率为 0

然后我们贪心,期望大的编号小。

CODE:

 1 #include<iostream>
 2 #include<cmath>
 3 #include<algorithm>
 4 #include<cstdio>
 5 using namespace std;
 6 
 7 int n,m,u[250005],v[250005],deg[505];
 8 double a[505][505],g[250005],res;
 9 
10 void gauss(){
11     for(int i=1,maxn=i;i<n;maxn=++i){
12         for(int j=i+1;j<=n;j++)
13             if(fabs(a[j][i])>fabs(a[maxn][i]))maxn=j;
14         for(int j=1;j<=n+1;j++)swap(a[i][j],a[maxn][j]);
15         for(int j=i+1;j<=n;j++){
16             if(fabs(a[j][i])<1e-10)continue;
17             double s=a[j][i]/a[i][i];
18             for(int k=1;k<=n+1;k++)a[j][k]-=a[i][k]*s;
19         }
20     }
21     for(int i=n;i>=1;i--){
22         for(int j=i+1;j<=n;j++)
23             a[i][n+1]-=a[i][j]*a[j][n+1];
24         a[i][n+1]/=a[i][i];
25     }
26 }
27 
28 int main(){
29     scanf("%d%d",&n,&m);
30     for(int i=1;i<=m;i++){
31         scanf("%d%d",u+i,v+i);
32         deg[u[i]]++,deg[v[i]]++;
33     }
34     a[1][n+1]=-1,a[n][n]=1;
35     for(int i=1;i<=m;i++){
36         if(u[i]^n)a[u[i]][v[i]]=1.0/deg[v[i]];
37         if(v[i]^n)a[v[i]][u[i]]=1.0/deg[u[i]];
38     }
39     for(int i=1;i<n;i++)a[i][i]=-1;
40     gauss();
41     for(int i=1;i<=m;i++)
42         g[i]=a[u[i]][n+1]/deg[u[i]]+a[v[i]][n+1]/deg[v[i]];
43     sort(g+1,g+m+1);
44     for(int i=1;i<=m;i++)res+=g[i]*(m-i+1);
45     printf("%.3f\n",res);
46 }

T2:【BZOJ3270】博物馆

Description

有一天Petya和他的朋友Vasya在进行他们众多旅行中的一次旅行,他们决定去参观一座城堡博物馆。这座博物馆有着特别的样式。它包含由mm条走廊连接的nn间房间,并且满足可以从任何一间房间到任何一间别的房间。
两个人在博物馆里逛了一会儿后两人决定分头行动,去看各自感兴趣的艺术品。他们约定在下午六点到一间房间会合。然而他们忘记了一件重要的事:他们并没有选好在哪儿碰面。等时间到六点,他们开始在博物馆里到处乱跑来找到对方(他们没法给对方打电话因为电话漫游费是很贵的)。
不过,尽管他们到处乱跑,但他们还没有看完足够的艺术品,因此他们每个人采取如下的行动方法:每一分钟做决定往哪里走,有Pi 的概率在这分钟内不去其他地方(即呆在房间不动),有1Pi1−Pi 的概率他会在相邻的房间中等可能的选择一间并沿着走廊过去。这里的i指的是当期所在房间的序号。在古代建造是一件花费非常大的事,因此每条走廊会连接两个不同的房间,并且任意两个房间至多被一条走廊连接。
两个男孩同时行动。由于走廊很暗,两人不可能在走廊碰面,不过他们可以从走廊的两个方向通行。(此外,两个男孩可以同时地穿过同一条走廊却不会相遇)两个男孩按照上述方法行动直到他们碰面为止。更进一步地说,当两个人在某个时刻选择前往同一间房间,那么他们就会在那个房间相遇。
两个男孩现在分别处在aa,bb两个房间,求两人在每间房间相遇的概率。

Input

第一行包含四个整数,nn表示房间的个数,mm表示走廊的数目,a,b(1a,bn)a,b(1 ≤ a, b ≤ n),表示两个男孩的初始位置。
之后mm行每行包含两个整数,表示走廊所连接的两个房间。
之后nn行每行一个至多精确到小数点后四位的实数,表示待在每间房间的概率。
题目保证每个房间都可以由其他任何房间通过走廊走到。

Output

输出一行包含nn个由空格分隔的数字,注意最后一个数字后也有空格,第ii个数字代表两个人在第i间房间碰面的概率(输出保留6位小数)
注意最后一个数字后面也有一个空格

题解:

这回是两个人,同样 id[i][j] 表示 Petya 在 i ,Vasya 在 j 。

设 a[id[x][y]][id[i][j]] 为从状态 id[i][j] 转移到 id[x][y] 的概率来列个方程组

假设现在在 id[i][j] 这个状态上,接下来有这么几种情况

1:两个人同时停留在原点, a[id[i][j]][id[i][j]]=p[i]*p[j] ;

2:第一个人走到了x(前提是i->x有边存在), a[id[x][j]][id[i][j]]+=(1-p[i])/du[i]*p[j] ;

3:第二个人走到了y, a[id[i][y]][id[i][j]]+=p[i]*(1-p[j])/du[j] ;

4:第一个人走到了x,第二个人走到了y, a[id[x][y]][id[i][j]]+=(1-p[i])/du[i]*(1-p[j])/du[j] ;

同上,把方程解出来即可。

CODE:

 1 #include<iostream>
 2 #include<cmath>
 3 #include<algorithm>
 4 #include<cstdio>
 5 using namespace std;
 6 
 7 int n,m,u[250005],v[250005],deg[505];
 8 double a[505][505],g[250005],res;
 9 
10 void gauss(){
11     for(int i=1,maxn=i;i<n;maxn=++i){
12         for(int j=i+1;j<=n;j++)
13             if(fabs(a[j][i])>fabs(a[maxn][i]))maxn=j;
14         for(int j=1;j<=n+1;j++)swap(a[i][j],a[maxn][j]);
15         for(int j=i+1;j<=n;j++){
16             if(fabs(a[j][i])<1e-10)continue;
17             double s=a[j][i]/a[i][i];
18             for(int k=1;k<=n+1;k++)a[j][k]-=a[i][k]*s;
19         }
20     }
21     for(int i=n;i>=1;i--){
22         for(int j=i+1;j<=n;j++)
23             a[i][n+1]-=a[i][j]*a[j][n+1];
24         a[i][n+1]/=a[i][i];
25     }
26 }
27 
28 int main(){
29     scanf("%d%d",&n,&m);
30     for(int i=1;i<=m;i++){
31         scanf("%d%d",u+i,v+i);
32         deg[u[i]]++,deg[v[i]]++;
33     }
34     a[1][n+1]=-1,a[n][n]=1;
35     for(int i=1;i<=m;i++){
36         if(u[i]^n)a[u[i]][v[i]]=1.0/deg[v[i]];
37         if(v[i]^n)a[v[i]][u[i]]=1.0/deg[u[i]];
38     }
39     for(int i=1;i<n;i++)a[i][i]=-1;
40     gauss();
41     for(int i=1;i<=m;i++)
42         g[i]=a[u[i]][n+1]/deg[u[i]]+a[v[i]][n+1]/deg[v[i]];
43     sort(g+1,g+m+1);
44     for(int i=1;i<=m;i++)res+=g[i]*(m-i+1);
45     printf("%.3f\n",res);
46 }

 

posted @ 2018-07-28 09:06  ezoiLZH  阅读(280)  评论(1编辑  收藏  举报