[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]}}(j\in{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\n",ans);
  return 0;
}
posted @ 2018-03-29 21:59  小蒟蒻ysn  阅读(427)  评论(0编辑  收藏  举报