bzoj3143 游走 概率 高斯消元

链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3143

题目大意是说:给出一张无向图,找出一种加权值的方式,使得从1到n期望带权路径长度最短,输出最短路长度。

首先,根据基本常识,走的次数多的边,权值越小越好(废话)。

于是问题转变为:找出每条边的期望经过次数。

设i边期望经过次数为f[i],则f[i]就为两个端点期望经过次数与走到这条边概率之商的和(没看懂?就是f[i]=x[duan1]/du[duan1]+x[duan2]/du[duan2],x[i]表示i点期望经过次数,du[i]表示i点在图中的度)。

问题进一步转化为:找出每个点的期望经历次数。

首先,由于我们走过终点就可以不走了,所以我们忽略掉n节点。

根据期望的线性性可以得出x[i]=sigma{(x[j]+1)/du[j]}。

把每个点的情况都弄出来,就会发现这是一个有n个变量,n个式子的线性方程组。

于是用死也打不对的高斯消元解决。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 const int maxn=505,maxm=500005;
 8 const double eps=1e-7;
 9 struct node
10 {
11     int from,to,next;
12 }edge[maxm];
13 int head[maxn],tot;
14 void addedge(int u,int v)
15 {
16     edge[++tot]=(node){u,v,head[u]};head[u]=tot;
17 }
18 int du[maxn],n,m;
19 double a[maxn][maxn],x[maxn],f[maxm];
20 void Gauss()
21 {
22     int im,num=1;
23     for(int k=1;k<=n;k++,num++)
24     {
25         im=k;
26         for(int i=k+1;i<=n;i++)
27             if(fabs(a[i][k])>fabs(a[im][k]))im=i;
28         if(im!=k)
29             for(int i=k;i<=n+1;i++)swap(a[num][i],a[im][i]);
30         if(fabs(a[num][k])==0)
31         {
32             num--;
33             continue;
34         }
35         for(int i=num+1;i<=n;i++)
36         {
37             if(!a[num][i])continue;
38             double t=a[i][k]/a[num][k];
39             for(int j=k;j<=n+1;j++)
40                 a[i][j]-=t*a[k][j];
41         }
42     }
43     for(int i=n;i>=1;i--)
44     {
45         for(int j=n;j>=i+1;j--)
46             a[i][n+1]-=x[j]*a[i][j];
47         x[i]=a[i][n+1]/a[i][i];
48     }
49 }
50 bool cmp(double a,double b)
51 {
52     return a>b;
53 }
54 int haha()
55 {
56     //freopen("walk.in","r",stdin);
57     //freopen("walk.out","w",stdout);
58     scanf("%d%d",&n,&m);
59     for(int i=1;i<=m;i++)
60     {
61         int a,b;scanf("%d%d",&a,&b);
62         du[a]++;du[b]++;
63         addedge(a,b);addedge(b,a);
64     }
65     a[1][n--]=-1;
66     for(int i=1;i<=n;i++)
67     {
68         a[i][i]=-1;
69         for(int j=head[i];j;j=edge[j].next)
70         {
71             int v=edge[j].to;
72             if(v!=n+1)
73                 a[i][v]+=(double)1/(du[v]);
74         }
75     }
76     Gauss();
77     for(int i=1;i<=tot;i++)
78         f[(i-1)>>1]+=x[edge[i].to]/du[edge[i].to];
79     sort(f,f+m,cmp);
80     double ans=0;
81     for(int i=0;i<m;i++)ans+=f[i]*(i+1);
82     printf("%0.3lf\n",ans);
83 }
84 int sb=haha();
85 int main(){;}

 

posted @ 2017-07-15 12:01  ccc000111  阅读(205)  评论(0编辑  收藏  举报