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\n",sum);
    return 0;
}
posted @ 2018-07-02 16:24  Monster_Qi  阅读(100)  评论(0编辑  收藏  举报