• 【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 imes 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
    ",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
    ",res);
    46 }
    作者:ezoiLZH
    本文版权归作者和博客园所有,欢迎转载,只要写明原文链接即可(^_^)。
  • 相关阅读:
    undo表空间
    SQL*Plus快速入门
    win10用命令net启动服务没权限解决办法
    Oracle表空间管理
    Oracle数据泵(上)
    windows的bat脚本
    Servlet映射细节
    JSP技术(一)
    690. Employee Importance
    BFS和DFS详解以及java实现(转载)
  • 原文地址:https://www.cnblogs.com/ezoiLZH/p/9380552.html
Copyright © 2020-2023  润新知