zoukankan      html  css  js  c++  java
  • 【洛谷3232】[HNOI2013] 游走(贪心+高斯消元)

    点此看题面

    大致题意: 一个无向连通图,小(Z)(1)号顶点出发,每次随机选择某条边走到下一个顶点,并将(ans)加上这条边的编号,走到(N)号顶点时结束。请你对边进行编号,使总分期望值最小。

    一个贪心的思想

    由于贪心的思想,我们肯定是给期望访问次数最大的边编号为(1),第二大的编号为(2),第三大的编号为(3),以此类推。

    那么我们应该怎么求出边的期望呢?

    由于边的期望可以由点的期望转化得来,因此只要求出了点的期望,就能求出边的期望。

    那么怎么求出点的期望呢?

    这时就需要用高斯消元了。

    如何求出点的期望

    下面是一张无向图。

    如果我们用(S_i)来表示编号为(i)的节点被经过的期望次数,那么显然:

    [S_1=frac{S_2}5+frac{S_3}3+frac{S_4}3+frac{S_5}2+frac{S_6}3+1 ]

    即编号为(x)的点的期望(S_x= sum frac{S_i}{deg_i}),其中(i)为与(x)有边相连的节点。

    像这样,我们可以将每一个点的期望都用其他点的期望来表示。

    还是以(S_1)为例,我们可以将这个式子移项:

    [S_1-frac{S_2}5-frac{S_3}3-frac{S_4}3-frac{S_5}2-frac{S_6}3=1 ]

    将每个式子都进行这样的转换之后,就可以通过高斯消元来求解出每一个(S_i)

    其中要注意的是,每一个式子中(S_n)的系数皆为(0)(因为游走在走到(n)号节点时结束),且第(1)个式子等号右边的值为(1)(因为游走从(1)号节点开始),而其他式子等号右边的值皆为(0)

    从点的期望到边的期望

    接下来的问题是,如何通过点的期望求出边的期望。

    (E_i)表示编号为(i)的边被经过的期望次数,且编号为(i)的边连接的两个节点为(x_i)(y_i),由于期望的性质,我们可以得到:

    [E_i=frac{S_{x_i}}{deg_{x_i}}+frac{S_{y_i}}{deg_{y_i}} ]

    这样就可以轻松求出每条边的期望了。

    然后,按照开头所述的贪心思想,就能轻松求解该题了。

    代码

    #include<bits/stdc++.h>
    #define max(x,y) ((x)>(y)?(x):(y))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define abs(x) ((x)<0?-(x):(x))
    #define LL long long
    #define ull unsigned long long
    #define swap(x,y) (x^=y,y^=x,x^=y)
    #define Fsize 100000
    #define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
    #define N 500
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].from=x,e[ee].to=y,++deg[x])
    char Fin[Fsize],*FinNow=Fin,*FinEnd=Fin;
    using namespace std;
    const double eps=1e-15;
    int n,m,ee=0,lnk[N+5],deg[N+5];
    struct edge
    {
        int from,to,nxt;
        double val;
    }e[N*N+5];
    inline bool cmp(edge x,edge y)
    {
        return x.val-y.val>eps;
    }
    struct Gauss//高斯消元
    {
        double a[N+5][N+5],s[N+5];
        inline void GetDataA(int x,int y,double v) {a[x][y]+=v;}
        inline void GetDataS(int x,double v) {s[x]=v;}
        inline void FindLine(int x)
        {
            register int i=x,j;register double t;
            while(fabs(a[i][x])<eps) ++i;
            for(t=s[i],s[i]=s[x],s[x]=t,j=1;j<=n;++j) t=a[i][j],a[i][j]=a[x][j],a[x][j]=t;
        }
        inline double GetAns()
        {
            register int i,j,k;register double delta,ans=0;
            for(i=1;i<n-1;++i)
            {
                FindLine(i);
                for(j=i+1;j<n;++j) for(s[j]+=(delta=-a[j][i]/a[i][i])*s[i],k=1;k<=n;++k) a[j][k]+=delta*a[i][k];
            }
            for(i=n-1;i;--i) 
            for(s[i]/=a[i][i],j=i-1;j;--j) s[j]-=a[j][i]*s[i];
            for(i=2;i<=ee;++(++i)) e[i].val=s[e[i].from]/deg[e[i].from]+s[e[i].to]/deg[e[i].to];
            for(sort(e+1,e+ee+1,cmp),i=1;i<=ee;++i) ans+=e[i].val*i;
            return ans;
        }
    }S;
    inline void read(int &x)
    {
        x=0;static char ch;
        while(!isdigit(ch=tc()));
        while(x=(x<<3)+(x<<1)+ch-48,isdigit(ch=tc()));
    }
    int main()
    {
        register int i,j,x,y;
        for(read(n),read(m),i=1;i<=m;++i) read(x),read(y),add(x,y),add(y,x);
        for(i=1;i<n;++i) for(S.GetDataA(i,i,1),j=lnk[i];j;j=e[j].nxt) if(e[j].to^n) S.GetDataA(i,e[j].to,-1.0/deg[e[j].to]);//根据上面推导出的式子,初始化高斯消元的式子
        return S.GetDataS(1,1),printf("%.3lf",S.GetAns()),0;//得出答案并输出
    }
    
  • 相关阅读:
    php开发中处理emoji表情和颜文字的兼容问题
    red入门学习笔记
    面向对象的三个基本特征(讲解)
    详解Ajax请求(四)——多个异步请求的执行顺序
    ASP.NET MVC模型绑定的6个建议,徐汇区网站设计 狼人:
    2010年度报告:是谁在编写Linux内核? 狼人:
    Kataspace:用HTML5和WebGL创建基于浏览器的虚拟世界 狼人:
    .NET Micro Framework 4.2 RC2发布!,徐汇区网站设计 狼人:
    SilveOS:基于Silverlight的Web操作系统,徐汇区网站设计 狼人:
    年轻人,你着什么急? 狼人:
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3232.html
Copyright © 2011-2022 走看看