【HNOI2013】BZOJ3143 游走题解(高斯消元+概率期望+DP)

题目:BZOJ3143.
题目大意:给定一张简单无向图,现在小Z从点 1 1 1出发,每次等概率随机游走到一个有边相连的点并且获得等于该边编号的分数.现在让你给 m m m条边编号,使得第一次走到点 n n n时分数的期望值最小.
1 ≤ n ≤ 500 1\leq n\leq 500 1n500.

首先很显然边的期望经过次数越大,它的编号就应该越小,问题变为求每条边的期望经过次数.

由于算出边的期望经过次数比较困难,我们可以转化为经过点的期望次数,然后通过点的期望经过次数除掉点的度来算出边的期望经过次数.设边 ( x , y ) (x,y) (x,y)的期望经过次数为 F x , y F_{x,y} Fx,y,点 x x x的期望经过次数为 f x f_x fx,点 x x x的度为 d e g [ x ] deg[x] deg[x],那么有:
F x , y = f x d e g [ x ] + f y d e g [ y ] F_{x,y}=\frac{f_x}{deg[x]}+\frac{f_y}{deg[y]} Fx,y=deg[x]fx+deg[y]fy

考虑DP,沿用上面 f f f的定义,很容易列出方程:
f x = ∑ ( y , x ) ∈ E f y d e g [ y ] f_x=\sum_{(y,x)\in E}\frac{f_y}{deg[y]} fx=(y,x)Edeg[y]fy

考虑这个DP有后效性,但是这里 n n n的点的方程合起来会成为一个线性方程组,所以高斯消元即可.时间复杂度 O ( n 3 ) O(n^3) O(n3).

这里需要注意两点,第一个是与 n n n有关点的贡献都不用算,最后直接 f n = 1 f_n=1 fn=1即可;另一个是 f 1 f_1 f1在原方程的基础上还要 + 1 +1 +1.

为什么别人都用邻接表存边啊…

代码如下:

#include<bits/stdc++.h>
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=500;
const double eps=0.0000001;

int n,m,deg[N+9],x[N*N+9],y[N*N+9];
double f[N*N+9],ans;      //数组开成f[N+9]WA了N发...
struct matrix{
  int n,m;
  double v[N+9][N+9];
}a;

void Gauss(matrix &a){
  int now=1,r,n=a.n;
  double t;
  for (int i=1;i<=n;++i){
  	for (r=now;r<=n;++r)
  	  if (fabs(a.v[r][i])>eps) break;
  	if (r>n) continue;
  	if (r^now) swap(a.v[r],a.v[now]);
    t=a.v[now][i];
    for (int j=1;j<=n+1;++j) a.v[now][j]/=t;
    for (int j=1;j<=n;++j)
      if (j^now){
      	t=a.v[j][i];
        for (int k=1;k<=n+1;++k)
          a.v[j][k]-=t*a.v[now][k];
      }
    ++now;
  }
}

Abigail into(){
  scanf("%d%d",&n,&m);
  for (int i=1;i<=m;++i){
    scanf("%d%d",&x[i],&y[i]);
    ++deg[x[i]];++deg[y[i]];
  }
}

Abigail work(){
  a.n=n-1;
  for (int i=1;i<=m;++i){
  	if (x[i]==n||y[i]==n) continue;
  	a.v[x[i]][y[i]]=1.0/deg[y[i]];
  	a.v[y[i]][x[i]]=1.0/deg[x[i]];
  }
  for (int i=1;i<n;++i)
    a.v[i][i]=-1.0;
  a.v[1][n]=-1.0;
  Gauss(a);
  for (int i=1;i<=m;++i){
    if (x[i]^n) f[i]+=a.v[x[i]][n]/deg[x[i]];
    if (y[i]^n) f[i]+=a.v[y[i]][n]/deg[y[i]];
  }
  sort(f+1,f+1+m);
  for (int i=1;i<=m;++i)
    ans+=f[i]*(m-i+1);
}

Abigail outo(){
  printf("%.3lf\n",ans);
}

int main(){
  into();
  work();
  outo();
  return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值