[期望DP+高斯消元] BZOJ3143: [Hnoi2013]游走

题意

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

题解

直接算题目要求的东西有点难办。
首先边权的分配与小Z随机游走没有任何关系,
如果我们能知道每条边经过的期望次数,按期望从大到小给边编号1~m即可。
问题来了,如何才能知道每条边期望走的次数呢?
这里需要一步很妙的转化,设E[i]为点i的期望访问次数,d[i]为点i的出度,E(x,y)表示边x–y的期望经过次数,则有:

E(x,y)=E[x]/d[x]+E[y]/d[y]

这样问题就转化为求E[i],建立期望之间的关系,高斯消元即可。

复杂度 O(n3)

#include
#include
#include
using namespace std;
const int maxn=505,maxe=maxn*maxn;
struct Edge{
    int x,y; double w;
    bool operator < (const Edge &b)const{
        return wint n,m,d[maxn];
double ans,E[maxn],K[maxn][maxn];
void Gauss(){
    n--;
    for(int i=1;i<=n;i++){
        int where=0;
        for(int j=i;j<=n;j++) if(fabs(K[j][i])>fabs(K[where][i])) where=j;
        for(int j=0;j<=n;j++) swap(K[where][j],K[i][j]);
        for(int j=i+1;j<=n;j++){
            double t=K[j][i]/K[i][i];
            for(int k=0;k<=n;k++) K[j][k]-=t*K[i][k];
        }
    }
    for(int i=n;i>=1;i--){
        E[i]=-K[i][0];
        for(int j=i+1;j<=n;j++) E[i]-=K[i][j]*E[j];
        E[i]/=K[i][i];
    }
    n++;
}
int main(){
    freopen("bzoj3143.in","r",stdin);
    freopen("bzoj3143.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) scanf("%d%d",&Es[i].x,&Es[i].y), d[Es[i].x]++, d[Es[i].y]++;
    for(int i=1;i<=m;i++) if(Es[i].x!=n&&Es[i].y!=n){
        K[Es[i].x][Es[i].y]+=1.0/d[Es[i].y];
        K[Es[i].y][Es[i].x]+=1.0/d[Es[i].x];
    }
    K[1][0]=1;
    for(int i=1;i<=n-1;i++) K[i][i]=-1;
    Gauss();
    for(int i=1;i<=m;i++) 
    Es[i].w=E[Es[i].x]/d[Es[i].x]+E[Es[i].y]/d[Es[i].y];
    sort(Es+1,Es+1+m);
    for(int i=1;i<=m;i++) ans+=Es[i].w*(m-i+1);
    printf("%.3lf",ans);
    return 0;
} 

你可能感兴趣的:(高斯消元)