jzoj3221 【HNOI2013】游走

来源:互联网 发布:360软件大全官方下载 编辑:程序博客网 时间:2024/06/10 11:42

Problem

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

比较简单的一题,连我都能想到。写一下主要是因为我觉得他包含了许多图+概率的问题与高斯消元要注意的地方。
设g[i]为经过i点的期望次数。
1. 将1点拆分是个不错的选择,让g[1]表示再一次经过1的期望,最后记得加上1。起点所带来的贡献在常数项中加上。
2. 终点是没有向外的贡献的,注意:为了不对最终的f造成影响,消元完毕后g[n]应强制设为0,正常消元完毕g[n]必定等于1,因为步数无穷大+连通,所以肯定进一次(不能出来。)
3. 高斯消元回代有搞基更方便的方法,具体见代码。

#include <cstdio>#include <iostream>#include <algorithm>#define maxn 510using namespace std;int n,m;double a[maxn][maxn],d[maxn],g[maxn],f[maxn*maxn];int e[maxn*maxn][2],use[maxn],cho[maxn];void AR(int x,int y) {    if (y!=n) a[x][y]+=1/d[y];    if (y==1) a[x][0]-=1/d[1];    if (x!=n) a[y][x]+=1/d[x];    if (x==1) a[y][0]-=1/d[1];}void gauss() {    for (int i=1; i<=n; i++) {        for (int j=1; j<=n; j++)  if (a[j][i]!=0 && use[j]==0) {            use[j]=1;            cho[i]=j;            double t=a[j][i],ts;            for (int k=0; k<=n; k++) a[j][k]/=t;            for (int z=1; z<=n; z++) if (use[z]==0 && a[z][i]!=0) {                t=a[z][i];                for (int k=0; k<=n; k++) a[z][k]=a[z][k]/t-a[j][k];            }            break;        }    }    for (int i=n; i; i--) {        g[i]=a[cho[i]][0]/a[cho[i]][i];        for (int j=1; j<=n; j++) a[j][0]-=a[j][i]*g[i];    }}int main() {    freopen("3221.in","r",stdin);    scanf("%d %d",&n,&m);    for (int i=1; i<=n; i++) a[i][i]=-1;    for (int i=1; i<=m; i++) {        scanf("%d %d",&e[i][0],&e[i][1]);        d[e[i][0]]++,d[e[i][1]]++;    }    for (int i=1; i<=m; i++) AR(e[i][0],e[i][1]);    gauss();    g[1]+=1; g[n]-=1; //g[n]=0;    for (int i=1; i<=m; i++) f[i]=g[e[i][0]]/d[e[i][0]]+g[e[i][1]]/d[e[i][1]];    sort(f+1,f+1+m);    double ans=0;    for (int i=1; i<=m; i++) ans+=i*f[m-i+1];    printf("%.3lf",ans);}
0 0
原创粉丝点击