• BZOJ 3143 HNOI2013 游走 高斯消元 期望


    这道题是我第一次使用高斯消元解决期望类的问题,首发A了,感觉爽爽的....

    不过笔者在做完后发现了一些问题,在原文的后面进行了说明。

    中文题目,就不翻大意了,直接给原题:

      一个无向连通图,顶点从1编号到N,边从1编号到M。 

      小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。 

      现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。

      输出最小的总分期望值。

    Solution:

      这题贪心很明显,哪条边走过次数的期望最大,它就应该获得最小的编号。

      所以假设我们已经求出了每条边走过的期望,我们就可以给它们并编上号了。

      怎么算出每条边走过的期望呢?

      每条边连接着两个点u,v,很明显的,当我们经过这条边,一定是从两个点中的某一个进入。

      所以走过边l的期望=走过u点的期望次数*从u点走到l上的概率+走过v点的期望次数*从v点走到l上的概率 (其中从i点走到它连接边的概率为1/d[i],d[i]为i的度数)

      即:E[l]=e[u]/d[u]+e[v]/d[v]

      可是我们只知道e[n]=0。但我们还知道这些点之间哪些是连通的,从而可以得出它们之间的关系:

      

      我们就可以利用这些点之间的关系建立起方程组,从而使用高斯消元求解。

      别忘了,点求解完还要带回到每条边上去哦....

      

      附Bzoj上的AC代码(codevs上过不了...我也不知道为什么...)

      

      1 /*
      2   Problem : Bzoj 3143 概率 & 高斯消元 
      3   Author : Robert Yuan
      4   Memory : 15604 kb
      5   Time : 628 MS    
      6   Result : Accept
      7 */
      8 #include <cmath> 
      9 #include <cstdio>
     10 #include <cstring>
     11 #include <cstdlib>
     12 #include <algorithm>
     13 
     14 using namespace std;
     15 
     16 #define maxn 520
     17 
     18 struct Node{
     19     int data,next;
     20 }node[maxn*maxn<<1];
     21 
     22 struct Edge{
     23     int u,v;
     24     double w;
     25 }edge[maxn*maxn<<1];
     26 
     27 #define now node[point].data
     28 #define then node[point].next
     29 
     30 int n,m,cnt;
     31 int head[maxn],deg[maxn];
     32 const double eps=1e-6;
     33 double w[maxn][maxn],rec_x[maxn],ans;
     34 
     35 bool cmp(const Edge A,const Edge B){
     36     return A.w>B.w;
     37 }
     38 
     39 inline int in(){
     40     int x=0;char ch=getchar();
     41     while(ch>'9' || ch<'0') ch=getchar();
     42     while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
     43     return x;
     44 }
     45 
     46 void add(int u,int v){
     47     node[++cnt].data=v;node[cnt].next=head[u];deg[u]++;head[u]=cnt;
     48     node[++cnt].data=u;node[cnt].next=head[v];deg[v]++;head[v]=cnt;
     49 }
     50 
     51 void prework(){
     52     n=in();m=in();
     53     int u,v;
     54     for(int i=1;i<=n;i++) head[i]=-1;
     55     for(int i=1;i<=m;i++)
     56         u=in(),v=in(),edge[i].u=u,edge[i].v=v,add(u,v); 
     57     int point;
     58     for(int i=1;i<=n;i++){
     59         w[i][i]=1;
     60         point=head[i];
     61         while(point!=-1){
     62             w[i][now]=-(double)1/deg[now];
     63             point=then;
     64         }
     65     }
     66     w[1][n+1]=1;
     67 }
     68 
     69 void Swap(int i,int j,int x){
     70     double t;
     71     for(int k=x+1;k<=n+1;k++)
     72         t=w[i][k],w[i][k]=w[j][k],w[j][k]=t;
     73 }
     74 
     75 void gauss(){
     76     int i,j;
     77     for(i=1,j=1;i<=n && j<=n;i++,j++){
     78         int max_r=i;
     79         for(int k=i+1;k<=n;k++)
     80             if(fabs(w[max_r][j])+eps<fabs(w[k][j]))
     81                 max_r=k;
     82         if(fabs(w[max_r][j])<eps){i--;continue;}
     83         if(max_r!=i) Swap(i,max_r,j);
     84         for(int k=i+1;k<=n;k++){
     85             double rate=w[k][j]/w[i][j];
     86             w[k][j]=0;
     87             for(int l=j+1;l<=n+1;l++)
     88                 w[k][l]-=w[i][l]*rate;
     89         }
     90     }
     91     
     92     for(int i=n;i>=1;i--)
     93         if(fabs(w[i][i])>eps){
     94             double ans_c=w[i][n+1];
     95             for(int k=i+1;k<=n;k++)
     96                 ans_c-=w[i][k]*rec_x[k];
     97             rec_x[i]=ans_c/w[i][i];
     98         }
     99 }
    100 
    101 void mainwork(){
    102     gauss();
    103     for(int i=1;i<=m;i++){
    104         edge[i].w=rec_x[edge[i].u]/deg[edge[i].u]+rec_x[edge[i].v]/deg[edge[i].v];
    105     }    
    106     sort(edge+1,edge+m+1,cmp);
    107     for(int i=1;i<=m;i++)
    108         ans+=edge[i].w*i;
    109     printf("%.3lf",ans);
    110 }
    111 
    112 int main(){
    113 #ifndef ONLINE_JUDGE
    114     freopen("x.in","r",stdin);
    115 #endif
    116     prework();
    117     mainwork();
    118     return 0;
    119 }
    View Code

    [以下是笔者后来发现的问题]

      首先感谢某位不愿意透露姓名的人堆同学复制了我的代码,然后代入了样例,结果:

      

      然后第三行是无解?可是答案却能跑出来...于是我傻了...开始胡乱吹逼[毕竟这么久没打了...]

      

      好吧,然后各种逼都被打败了...(●—●)

      只能认真看看到底出了什么问题,于是发现这个式子有奥妙:

      

      左边的e[i]表示走到i点的期望,右边的e[j]表示走出j点的期望。

      “走到”和“走出”却并不是一样的!

      我们设e[i]表示走到i点的概率,e'[i]表示走出i点的概率。

      如果说i!=n那么走到就能走出,e[i]=e'[i];

      如果i==n那么就有e'[n]=0,e[n]=1他们俩不同...尽管都已知,而我们列式子的时候,将e'[n]作为未知数带进别的点的式子里,但在n自己的式子中却用的是e[n],导致两个变量混淆。

      所以鉴于e'[n]=0,就将建立方程部分修改了一下:

      

      于是现在的式子就发生了变化,最后化出来的矩阵也变成了正常的样子:

      

      这样解出来的就是e[n]是到达n点的期望=1,当然在给边设定权值的时候,我们用的都是e'[i],所以我们手动修改一下e'[n]=0就好了...

      下面是修改过后的代码:

    #include <cmath> 
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <algorithm>
     
    using namespace std;
     
    #define maxn 520
     
    struct Node{
        int data,next;
    }node[maxn*maxn<<1];
     
    struct Edge{
        int u,v;
        double w;
    }edge[maxn*maxn<<1];
     
    #define now node[point].data
    #define then node[point].next
     
    int n,m,cnt;
    int head[maxn],deg[maxn];
    const double eps=1e-6;
    double w[maxn][maxn],rec_x[maxn],ans;
     
    bool cmp(const Edge A,const Edge B){
        return A.w>B.w;
    }
     
    inline int in(){
        int x=0;char ch=getchar();
        while(ch>'9' || ch<'0') ch=getchar();
        while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
        return x;
    }
     
    void add(int u,int v){
        node[++cnt].data=v;node[cnt].next=head[u];deg[u]++;head[u]=cnt;
        node[++cnt].data=u;node[cnt].next=head[v];deg[v]++;head[v]=cnt;
    }
     
    void prework(){
        n=in();m=in();
        int u,v;
        for(int i=1;i<=n;i++) head[i]=-1;
        for(int i=1;i<=m;i++)
            u=in(),v=in(),edge[i].u=u,edge[i].v=v,add(u,v); 
        int point;
        for(int i=1;i<=n;i++){
            w[i][i]=1;
            point=head[i];
            while(point!=-1){
                if(now!=n) w[i][now]=-(double)1/deg[now];
                else w[i][now]=0;
                point=then;
            }
        }
        w[1][n+1]=1;
    }
     
    void Swap(int i,int j,int x){
        double t;
        for(int k=x+1;k<=n+1;k++)
            t=w[i][k],w[i][k]=w[j][k],w[j][k]=t;
    }
     
    void gauss(){
        int i,j;
        for(i=1,j=1;i<=n && j<=n;i++,j++){
            int max_r=i;
            for(int k=i+1;k<=n;k++)
                if(fabs(w[max_r][j])+eps<fabs(w[k][j]))
                    max_r=k;
            if(fabs(w[max_r][j])<eps){i--;continue;}
            if(max_r!=i) Swap(i,max_r,j);
            for(int k=i+1;k<=n;k++){
                double rate=w[k][j]/w[i][j];
                w[k][j]=0;
                for(int l=j+1;l<=n+1;l++)
                    w[k][l]-=w[i][l]*rate;
            }
        }
         
        for(int i=n;i>=1;i--)
            if(fabs(w[i][i])>eps){
                double ans_c=w[i][n+1];
                for(int k=i+1;k<=n;k++)
                    ans_c-=w[i][k]*rec_x[k];
                rec_x[i]=ans_c/w[i][i];
            }
        rec_x[n]=0;
    }
     
    void mainwork(){
        gauss();
        for(int i=1;i<=m;i++){
            edge[i].w=rec_x[edge[i].u]/deg[edge[i].u]+rec_x[edge[i].v]/deg[edge[i].v];
        }    
        sort(edge+1,edge+m+1,cmp);
        for(int i=1;i<=m;i++)
            ans+=edge[i].w*i;
        printf("%.3lf",ans);
    }
     
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("x.in","r",stdin);
    #endif
        prework();
        mainwork();
        return 0;
    }
    View Code

    最后再次鸣谢人堆同学提出的问题,有问题才有进步嘛,欢迎大家提问哦...

  • 相关阅读:
    提高mysql千万级大数据SQL查询优化30条经验(Mysql索引优化注意)
    五维偏序模板(bitset+分块)
    【BZOJ5328】【SDOI2018】—物理实验(扫描线+双指针)
    【VIOLET3】【BZOJ2716】天使玩偶(cdq分治)
    【BZOJ3295】【CQOI2011】—动态逆序对(cdq分治)
    【BZOJ1492】【NOI2007】—Cash(cdq分治维护凸包优化斜率dp)
    【BJOI2018】【BZOJ2591】—链上二次求和(线段树维护二次函数)
    【COGS2479】【HZOI2016】偏序(cdq套cdq)
    【BZOJ5293】【BJOI2018】—求和
    【BZOJ4009】【HNOI2015】—接水果(整体二分+扫描线)
  • 原文地址:https://www.cnblogs.com/Robert-Yuan/p/4636355.html
Copyright © 2020-2023  润新知