zoukankan      html  css  js  c++  java
  • [HNOI2013]游走

    https://zybuluo.com/mdeditor#1089748

    标签(空格分隔): 期望 高斯消元


    题面

    一个无向连通图,顶点从1编号到N,边从1编号到M。 小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。 现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。


    解析

    要使期望值最小,就让经过概率越大的边标号越小。
    那么就要求边的概率。

    设P[i]为点概率,E[i]为边概率,in[i]为点度数。
    (E[i]=sum{frac{P[i]}{in[i]}+frac{P[j]}{in[j]}}(i,j为边的两端点))

    于是要求点的概率。
    点的概率为相邻点转移到自己的概率。
    (P[i]=sum{frac{P[j]}{in[j]}}(j为i的邻点))

    待会,求当前点概率就要用到邻点概率,求邻点概率就要用到当前点概率,这没法DP啊。
    但我们可以发现一个表达式:((frac{1}{概率}=期望)
    (P[i]-sum{frac{1}{in[j]}}(jin{i的邻点})=[i==1])(出发点自身就有概率为1)
    这不很像一个一元一次方程?据此可解得(p[i])

    还有些细节,代码里应该有注释。

    // luogu-judger-enable-o2
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #define ll long long
    #define re register
    #define il inline
    #define fp(i,a,b) for(re int i=a;i<=b;i++)
    #define fq(i,a,b) for(re int i=a;i>=b;i--)
    using namespace std;
    const int N=600;
    int n,m,h[N*N<<1],cnt,in[N],st[N*N<<1],ed[N*N<<1];
    double dp[N][N],ans,x[N],E[N*N<<1];
    struct Edge
    {
      int to,next;
    }e[N*N<<1];
    il void add(re int u,re int v)
    {
      e[++cnt]=(Edge){v,h[u]};h[u]=cnt;
    }
    il int gi()
    {
      re int x=0,t=1;
      re char ch=getchar();
      while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    il void Gauss()
    {
      fp(i,1,n)
        fp(j,i+1,n)
        fq(k,n+1,i) dp[j][k]-=dp[i][k]*dp[j][i]/dp[i][i];
      fq(i,n,1)
        {
          x[i]=dp[i][n+1];
          fq(j,n,i+1) x[i]-=dp[i][j]*x[j];
          x[i]/=dp[i][i];
        }
    }
    int main()
    {
      memset(h,-1,sizeof(h));
      n=gi();m=gi();
      fp(i,1,m)
        {
          re int u=gi(),v=gi();add(u,v);add(v,u);in[u]++;in[v]++;st[i]=u;ed[i]=v;
        }
      fp(i,1,n-1)
        {
          dp[i][i]=1;
        for(re int j=h[i];j+1;j=e[j].next)
          {
        re int v=e[j].to;//j->i???
        if(v!=n) dp[i][v]-=1.0/in[v];//概率不能从n转移,写1.0!!!
          }
        }
      dp[1][n+1]=1;//钦定第一个点概率为1
      dp[n][n]=1;
      Gauss();
      fp(i,1,m) E[i]=x[st[i]]/in[st[i]]+x[ed[i]]/in[ed[i]];
      sort(E+1,E+1+m);
      fp(i,1,m) ans+=E[i]*(m-i+1);
      printf("%.3lf
    ",ans);
      return 0;
    }
    
  • 相关阅读:
    支付宝面试题(顶级互联网公司面试题系列)
    反应器模式 vs 生产者消费者模式
    反应器模式 vs 观察者模式
    Future Promise 模式(netty源码9)
    Pipeline inbound(netty源码7)
    Pipeline outbound
    Pipeline(netty源码)
    顺丰面试题(2018 顶级互联网公司面试题系列)
    【转】 linux编程之GDB调试
    【转】 linux内存管理
  • 原文地址:https://www.cnblogs.com/yanshannan/p/8672739.html
Copyright © 2011-2022 走看看