[HNOI2013] 游走

题意:

一张无向图,从点1开始随机游走,走到点n时结束。每走一条边会得到等同于边权的收益。

(随机游走:每次等概率选择一条当前点的出边走过去)

请你给m条边分配边权(边权是1-m的排列),使得期望收益最小。

$n\leq 500$。

 

题解:

令期望收益为S,边$(u,v)$的边权为$w_{u,v}$,期望经过次数为$cnt_{u,v}$,则有$S=\sum cnt_{u,v}\times w_{u,v}$。

发现$cnt_{u,v}$并不好求,但如果知道了每个点u的期望经过次数$F_u$和度数$D_u$,就有$cnt_{u,v}=\frac{F_u }{D_u }+\frac{F_v }{D_v }$。

而$F_1 =1+\sum \frac{F_v }{D_v },F_u =\sum \frac{F_v }{D_v }$,于是可以用高斯消元解出所有的F,然后反推cnt,倒序分配边权即可。

注意点n是不能走出去的,所以列方程时需要特判。

复杂度$O(n^{3})$。

 

套路:

  • 求边经过次数$\rightarrow$求点经过次数。
  • 随机游走问题:在列方程时注意终点是不能往外走的。

 

代码:

#include<bits/stdc++.h>
#define maxn 505
#define maxm 500005
#define inf 0x7fffffff
#define ll long long
#define rint register int
#define debug(x) cerr<<#x<<": "<<x<<endl
#define fgx cerr<<"--------------"<<endl
#define dgx cerr<<"=============="<<endl

using namespace std;
int D[maxm],G[maxn][maxn];
double A[maxn][maxn],res[maxm],F[maxm];

inline int read(){
    int x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}

inline void Gauss(int n){
    for(int j=1;j<=n;j++){
        for(int i=j;i<=n;i++)
            if(A[i][j]!=0){
                for(int k=1;k<=n+1;k++) swap(A[i][k],A[j][k]);
                break;
            }
        if(A[j][j]==0) continue;
        for(int i=1;i<=n;i++)
            if(i!=j && A[i][j]!=0){
                double x=A[i][j]/A[j][j];
                for(int k=1;k<=n+1;k++) A[i][k]-=A[j][k]*x;
            }
    }
}

int main(){
    int n=read(),m=read(),tot=0;
    for(int i=1;i<=m;i++){
        int u=read(),v=read();
        G[u][v]=G[v][u]=1,D[u]++,D[v]++;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(i==j) A[i][j]=1;
            else if(j==n) A[i][j]=0;
            else if(G[i][j]) A[i][j]=-1.0/(D[j]+0.0);
            else A[i][j]=0;
        }
        A[i][n+1]=(i==1)?1:0;
    }
    Gauss(n);
    for(int i=1;i<=n;i++) F[i]=A[i][n+1]/A[i][i];
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++){
            if(!G[i][j]) continue;
            if(j==n) res[++tot]=F[i]/(D[i]+0.0);
            else res[++tot]=F[i]/(D[i]+0.0)+F[j]/(D[j]+0.0);
        }
    sort(res+1,res+1+tot);
    double ans=0;
    for(int i=1;i<=m;i++) ans+=res[i]*(m-i+1);
    printf("%.3lf\n",ans);
    return 0;
}
游走

 

posted @ 2020-07-02 23:14  Fugtemypt  阅读(151)  评论(0编辑  收藏  举报