[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\not=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\n",ans);
    return 0;
}
posted @ 2019-02-11 13:42  ylsoi  阅读(212)  评论(0编辑  收藏  举报