[bzoj3143][Hnoi2013]游走——动态规划+高斯消元

题目大意:

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

思路:

由于我们需要确定每条边的权值,所以不妨将每条边期望意义下需要走的次数给求出来。

先考虑每条边单独考虑的时候要怎么做,设(f_u)表示考虑第(j)条边的时候从(u)出发期望意义下还要走多少次(j)边,不难发现直接动态规划状态转移会有环,于是可以对于每一个点都列一个方程组,(n^3)高斯消元解出1号点的答案即可。

但是每条边单独考虑的话,最多可能有(n^2)条边,那么时间复杂度就达到了(n^5),显然不是这样做。

考虑将边的问题转化到点上面,我们求出每个点期望意义下经过多少次,那么每条边((u,v))期望意义下经过的次数就是(frac{f_u}{d_u}+frac{f_v}{d_v}),这样以后我们就只需要做一次(n^3)的高斯消元了。

(f_{u})为点(u)期望意义下需要经过多少次:

[f_{u}=sum_{v,v ot=n}frac{f_{v}}{d_v} ]

当然1号点和(n)号点需要特殊考虑。

/*=======================================
 * Author : ylsoi
 * Time : 2019.2.11
 * Problem : bzoj3143
 * E-mail : ylsoi@foxmail.com
 * ====================================*/
#include<bits/stdc++.h>
 
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;
 
using namespace std;
 
void File(){
    freopen("bzoj3143.in","r",stdin);
    freopen("bzoj3143.out","w",stdout);
}
 
template<typename T>void read(T &_){
    _=0; T f=1; char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
    _*=f;
}
 
const int maxn=500+10;
const double eps=1e-7;
int n,m,d[maxn];
int beg[maxn],to[maxn*maxn*2],las[maxn*maxn*2],cnte=1;
double f[maxn][maxn],p[maxn],e[maxn*maxn],ans;
 
void add(int u,int v){
    las[++cnte]=beg[u],beg[u]=cnte,to[cnte]=v;
    las[++cnte]=beg[v],beg[v]=cnte,to[cnte]=u;
}
 
void print(){
    REP(i,1,n){
        REP(j,1,n+1)printf("%lf ",f[i][j]);
        cout<<endl;
    }
    cout<<"====================================="<<endl;
}
 
void Gauss(){
    //print();
    REP(i,1,n-1){
        REP(j,i+1,n)if(fabs(f[j][i])>fabs(f[i][i]))swap(f[i],f[j]);
        REP(j,i+1,n){
            double t=f[j][i]/f[i][i];
            REP(k,i,n+1)f[j][k]=f[j][k]-f[i][k]*t;
        }
        //print();
    }
    DREP(i,n,1){
        p[i]=f[i][n+1]/f[i][i];
        REP(j,1,i-1)f[j][n+1]-=f[j][i]*p[i];
    }
    //REP(i,1,n)cout<<p[i]<<endl;
    //cout<<"================================"<<endl;
    REP(i,1,m){
        int u=to[i<<1],v=to[i<<1|1];
        e[i]=p[u]/d[u]*(u!=n)+p[v]/d[v]*(v!=n);
        //cout<<u<<" "<<v<<" "<<e[i]<<endl;
    }
}
 
int main(){
    //File();
    read(n),read(m);
    int u,v;
    REP(i,1,m)read(u),read(v),add(u,v),++d[u],++d[v];
    REP(i,1,n){
        f[i][i]=1;
        if(i==1 || i==n)f[i][n+1]=1;
        if(i==n)continue;
        for(int j=beg[i];j;j=las[j]){
            v=to[j];
            if(v==n)continue;
            f[i][v]=-1.0/d[v];
        }
    }
    Gauss();
    sort(e+1,e+m+1);
    REP(i,1,m)ans+=e[i]*(m-i+1);
    printf("%.3lf
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/ylsoi/p/10361586.html