• BZOJ 3143: [Hnoi2013]游走


    Time Limit: 10 Sec Memory Limit: 128 MB
    Submit: 3767 Solved: 1710
    [Submit][Status][Discuss]
    Description

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

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

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

    2 3

    1 2

    1 3

    Sample Output
    3.333
    HINT

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

    解题思路

    因为要求得最小得分,所以应该将经过概率小的边赋值最大,要想求边的概率就要先求点的概率,点的概率等于1./其余能到达这个点的点的度数(貌似有点绕),因为可能出现环状,所以不能直接递推,列出式子后发现可以高斯消元。之后边的概率等于两点的概率除以度数。注意求点时不考虑n,因为n不能到其他点,而处置应该是x[1][n]=1,因为刚开始在1点。
    注意数组大小!!!!!!本蒟蒻就因为数组大小调了两个小时,多亏radish巨的提醒。

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    
    using namespace std;
    const int MAXN = 505;
    const double eps = 1e-7;
    
    inline int rd(){
        int xx=0,f=1;char ch=getchar();
        while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)) {xx=(xx<<1)+(xx<<3)+ch-48;ch=getchar();}
        return xx*f;
    }
    
    int n,m,cnt,head[MAXN];
    int wx[MAXN*MAXN],wy[MAXN*MAXN];
    int where[MAXN];
    double ans[MAXN],du[MAXN];
    double Ans[MAXN*MAXN],sum;
    double x[MAXN][MAXN];   //gauss 
    
    struct Edge{
        int to,nxt;
    }edge[MAXN*MAXN<<1];
    
    inline void add(int bg,int ed){
        edge[++cnt].to=ed;
        edge[cnt].nxt=head[bg];
        head[bg]=cnt;
    }
    
    inline void gauss(){
        n--;
        for(register int i=1;i<=n;i++){
            int p=-1;
            double mx=0;
            for(register int j=1;j<=n;j++)
                if(fabs(x[i][j])-eps>mx)
                    mx=fabs(x[i][j]),p=j;
            where[i]=p;
            for(register int j=1;j<=n;j++){
                if(i==j) continue;
                double t=x[j][p]/x[i][p];
                for(register int k=1;k<=n+1;k++)
                    x[j][k]-=t*x[i][k];
            }
        }
        for(register int i=1;i<=n;i++)
            ans[where[i]]=x[i][n+1]/x[i][where[i]];
        n++;
    }
    
    int main(){
        n=rd();m=rd();
        for(register int i=1;i<=m;i++){ 
            int xx,y;
            xx=rd();y=rd();
            add(xx,y);add(y,xx);
            du[xx]+=1.0;du[y]+=1.0;
            wx[i]=xx;wy[i]=y;
        }
        for(register int i=1;i<n;i++){
            x[i][i]=1.0;
            for(register int j=head[i];j;j=edge[j].nxt)
                if(edge[j].to!=n)
                    x[i][edge[j].to]=-1.0/du[edge[j].to];
        }
        x[1][n]=1.0;
    //  for(register int i=2;i<n;i++) x[i][n]=0;
    //  for(register int i=1;i<n;i++){
    //      for(register int j=1;j<=n;j++)
    //          cout<<x[i][j]<<" ";
    //      cout<<endl; 
    //  }
        gauss();
    //  for(register int i=1;i<n;i++)
    //      cout<<ans[i]<<" ";
    //  cout<<endl;
        for(register int i=1;i<=m;i++){
            int u=wx[i];
            int v=wy[i];
            Ans[i]=ans[u]/du[u]+ans[v]/du[v];
        }
        sort(Ans+1,Ans+1+m);
    //  for(register int i=1;i<=m;i++) cout<<Ans[i]<<" ";
        int gg=m;
        for(register int i=1;i<=m;i++){
            sum+=(double)((double)gg*Ans[i]);
            gg--;
    //      cout<<sum<<endl;
        }
        printf("%.3lf
    ",sum);
        return 0;
    }
  • 相关阅读:
    注解的那些事儿(三)| 注解的使用
    注解的那些事儿(二)| 如何自定义注解
    注解的那些事儿(一)| 为什么要使用注解?
    数据库历险记(三) | 缓存框架的连环炮
    书值 | 第 2 期:成为技术管理者,思维上应该如何转变?
    书值 | 第 1 期:如何在1年内完成大学四年的课程?
    网站被篡改详细处理方法
    【代码审计】任意文件读取漏洞实例
    XSS三重URL编码绕过实例
    代码审计之DocCms漏洞分析
  • 原文地址:https://www.cnblogs.com/sdfzsyq/p/9676998.html
Copyright © 2020-2023  润新知