bzoj3143 [Hnoi2013]游走 (期望概率DP + 高斯消元)

来源:互联网 发布:淘宝c店详情页banner 编辑:程序博客网 时间:2024/05/17 23:42

bzoj3143 [Hnoi2013]游走

原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=3143

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

数据范围
2≤N≤500且是一个无向简单连通图。

题解:
这里提供了一种用高斯消元解方程来解决有后效性问题的思路。

我们要得到每条边的期望经过次数,然后把期望最大的排名尽量小
把求没条边的期望经过次数转化为每个点的期望经过次数,
则 令 l[i]为一条边的期望经过次数,f[u]为一个点的期望经过次数,
那么: l[i]=f[u]* (1/degree(u))+f[v]*(1/degree(v))
特殊地,对于一个端点为n的点,要忽略点n,因为不可能从点n过来

DP方程应该很容易得到:
f[u]=∑( f[v]*(1/degree(v))) (v与u有边)
特殊地
f[1]=∑( f[v]*(1/degree(v))) +1 (1本来经过了一次)
对于与n有边的点 f[u]=∑( f[v]*(1/degree(v))) (v!=n, u与点n有边)
f[n]用不到。
发现有后效性,这个DP不了诶。
但是我们呢得到了一个 关于 f[i]的,(n-1)元一次方程组。
于是 高斯消元解方程,得到f[i]求l[i],得到答案。
发现自己的高消快忘了……

代码:

#include<cstdio>#include<iostream>#include<cstring>#include<cmath>#include<algorithm>using namespace std;int n,m;const int N=505;const double EPS=1e-10;int head[N],to[2*N*N],nxt[2*N*N],du[N],num=0;int uu[N*N],vv[N*N];double a[N][N],f[N],l[2*N*N];void build(int u,int v){    num++;    to[num]=v;    nxt[num]=head[u];    head[u]=num;}void gauss(){    int cnt=0;     for(int i=1;i<=n;i++)     {        int j=-1; double mx=EPS;        for(int k=cnt+1;k<=n;k++)        {            if(fabs(a[k][cnt+1])>mx)            {                mx=fabs(a[k][cnt+1]); j=k;            }        }        if(j==-1) continue;        for(int k=1;k<=n+1;k++)        swap(a[cnt+1][k],a[j][k]);        for(int j=1;j<=n;j++)        {            if(j==cnt+1) continue;            if(fabs(a[j][i])<EPS) continue;            double r=a[j][i]/a[cnt+1][i];            for(int k=1;k<=n+1;k++)            {                a[j][k]-=a[cnt+1][k]*r;            }        }        cnt++;      }    for(int i=1;i<=n;i++)    {        f[i]=a[i][n+1]/a[i][i];    }   }bool cmp(const double &A,const double &B){    return A-B>EPS;}int main(){    scanf("%d%d",&n,&m);    for(int i=0;i<=n+1;i++)    for(int j=0;j<=n+1;j++)     a[i][j]=0.0;    for(int i=1;i<=m;i++)    {        int u,v;        scanf("%d%d",&u,&v);        uu[i]=u; vv[i]=v;        build(u,v); build(v,u);        du[u]++; du[v]++;     }    n--;    for(int i=1;i<=n;i++)    {        a[i][i]=-1.0;        if(i==1) a[i][n+1]=-1.0;        for(int j=head[i];j;j=nxt[j])        {                       int v=to[j];            if(v==n+1) continue;            a[i][v]=(double)1.0/du[v];              }    }    gauss();    n++;    for(int i=1;i<=m;i++)    {        l[i]=(double)f[uu[i]]/du[uu[i]]+(double)f[vv[i]]/du[vv[i]];    }    sort(l+1,l+m+1,cmp);    double ans=0;    for(int i=1;i<=m;i++)    ans+=(double)l[i]*i;    printf("%0.3lf\n",ans);    return 0;}
原创粉丝点击