• [HNOI2013]游走


    题目描述

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

    输入输出格式

    输入格式:

    第一行是正整数N和M,分别表示该图的顶点数 和边数,接下来M行每行是整数u,v(1<=u,v<=N),表示顶点u与顶点v之间存在一条边。 输入保证30%的数据满足N<=10,100%的数据满足2<=N<=500且是一个无向简单连通图。

    输出格式:

    仅包含一个实数,表示最小的期望值,保留3位小数。

    输入输出样例

    输入样例#1: 
    3 3
    2 3
    1 2
    1 3
    输出样例#1: 
    3.333

    说明

    边(1,2)编号为1,边(1,3)编号2,边(2,3)编号为3。

    我们发现每条边对答案的贡献就是走这条边的期望(Σ次数*概率)*这条边的编号。

    最优方案显然是求出所有期望之后大的期望对应小的编号。。。

    但是怎么求边的期望呢???

    边的期望直接求不好求,可以考虑从点的求得。

    设E(x)为走x点的期望(Σ次数*概率),E(u,v)为走这条边的期望。

    那么E(x,y)=E(x)/d(x)+E(y)/d(y),d(i)表示i点的度数。

    当然如果x,y其中一个是n的话就不能算了,因为到了n就结束了,不可能再走这条边。

    所以现在问题就变成了如何求每个点的期望。

    显然E(n)=1,因为n是游戏的终点,最多也最少走一次。

    对于其他的x,E(x)=ΣE(v)/d(v) ,条件是存在边(x,v)。

    特殊的,对于E(1),右式还要+1,因为1是起点。

    这是一个有后效性的方程,我们只能通过高斯消元求解。。。

    (好像实数高斯消元的精度误差都很毒瘤,,,现在已经很迷茫eps到底取多少了hhhh)

    #include<bits/stdc++.h>
    #define ll long long
    #define D long double
    #define maxn 505
    #define maxm 250005
    using namespace std;
    const D eps=10e-11;
    D a[maxn][maxn],tot;
    D ap[maxm],ans[maxn];
    int n,m,u[maxm];
    int v[maxm],d[maxn];
    bool g[maxn][maxn];
    
    inline int zt(D x){
        if(x>=-eps&&x<=eps) return 0;
        return x>0?1:-1;
    }
    
    inline void solve(){
        for(int i=1;i<n;i++){
        	int o=i;
        	for(int j=i+1;j<n;j++) if(zt(a[j][i]-a[o][i])==1) o=j;
        	
        	if(o!=i)
        	    for(int k=i;k<=n;k++) swap(a[o][k],a[i][k]);
            
            for(int j=i+1;j<n;j++) if(zt(a[j][i])){
                D base=a[j][i]/a[i][i];
                for(int k=i;k<=n;k++) a[j][k]-=a[i][k]*base;
            }
        }
        
    //	ans[n]=1.00;
        for(int i=n-1;i;i--){
            for(int j=n-1;j>i;j--) a[i][n]-=ans[j]*a[i][j];
            ans[i]=a[i][n]/a[i][i];
        }
    }
    
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++){
            scanf("%d%d",u+i,v+i);
            d[u[i]]++,d[v[i]]++;
            g[u[i]][v[i]]=g[v[i]][u[i]]=1;
        }
        
        for(int i=1;i<n;i++){
            a[i][i]=1.00;
            for(int j=1;j<n;j++) if(g[j][i]) a[i][j]=-1.00/(D)d[j];
        }
        a[1][n]=1.00;
        
        solve();
        
        for(int i=1;i<=m;i++) ap[i]=ans[u[i]]/d[u[i]]+ans[v[i]]/d[v[i]];
        sort(ap+1,ap+m+1);
        for(int i=1;i<=m;i++) tot+=ap[i]*(m-i+1.00);
        
        printf("%.3Lf
    ",tot);
        return 0;
    }
    

      

  • 相关阅读:
    [置顶] 移动应用不得不看的三张图
    重新签名apk文件(手工用命令行)
    o(n)解决问题:调整数组顺序是奇数位于偶数的前面
    安装DBMS_SHARED_POOL包
    sql server 实现sleep延时
    【Unity 3D】使用 2DToolkit 插件 制作2D精灵动画
    消息对话框(MessageBox)用法介绍
    java监控之ManagementFactory分析
    Java内存模型深度解析:总结--转
    Java内存模型深度解析:final--转
  • 原文地址:https://www.cnblogs.com/JYYHH/p/8448087.html
Copyright © 2020-2023  润新知