bzoj3143 [Hnoi2013]游走

这个题是让你给遍赋权,问1-n路径的最小期望值。

显然只要我们知道了每一条边经过次数的期望值,我们就可以贪心地给边赋权了。但是要求边的期望值会很恶心,因为每条边正着走和反着走对其它边的贡献是不同的。于是我们考虑求每个点经过的次数的期望,那么每条边经过次数的期望就是它的端点的期望/端点的度数。

设f[i]表示i点经过次数的期望,那么f[i]=Σf[j]/d[j]  (j->i)

注意一下边界条件,由于一开始就在1号点,所以f[1]=1+Σf[j]/d[j],而虽然n点会被经过好多次,但是n点对它连接的边是不会有贡献的,所以f[n]=0。

这样我们记可以高斯消元了。最后贪心赋边权,得到答案。

walk
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cmath>
  5 #include<cstring>
  6 #define maxn 1000
  7 #define maxm 500000
  8 #define eps 1e-9
  9 #define inf 1000000000
 10 using namespace std;
 11 typedef double lb;
 12 struct et
 13 {
 14     int s,t;
 15 }e[maxm];
 16 lb a[maxn][maxn],v[maxn],w[maxm];
 17 int d[maxn],t[maxn][maxn];
 18 int n,m;
 19 
 20 lb fab(lb x)
 21 {
 22     return (x>0)?x:-x;
 23 }
 24 void gauss(int n)
 25 {
 26     int k=1;
 27     for (int i=1;i<=n;i++)
 28     {
 29         int p=0;
 30         for (int j=k;j<=n;j++)
 31             if (fab(a[i][j])>eps) {    p=j; break;    }
 32         if (!p) continue;
 33         for (int l=1;l<=n+1;l++) swap(a[p][l],a[k][l]);
 34         for (int j=k+1;j<=n;j++)
 35         {
 36             lb rate=a[j][i]/a[k][i];
 37             for (int l=1;l<=n+1;l++) 
 38                 a[j][l]-=a[k][l]*rate;
 39         }
 40         k++;
 41     }
 42     for (int i=n;i;i--)
 43     {
 44         v[i]=a[i][n+1];
 45         for (int j=i+1;j<=n;j++)
 46             v[i]-=v[j]*a[i][j];
 47         v[i]/=a[i][i];
 48     }
 49 }
 50 
 51 void debug()
 52 {
 53     for (int i=1;i<=n;i++)
 54     {
 55         for (int j=1;j<=n+1;j++)
 56             cout<<a[i][j]<<' ';
 57         cout<<endl;
 58     }
 59 }
 60 
 61 bool cmp(lb a,lb b)
 62 {
 63     return a>b;
 64 }
 65 
 66 int main()
 67 {
 68     scanf("%d%d",&n,&m);
 69     int x,y;
 70     for (int i=1;i<=m;i++)
 71     {
 72         scanf("%d%d",&x,&y);
 73         e[i].s=x; e[i].t=y;
 74         t[x][++d[x]]=y;
 75         if (x==y) continue;
 76         t[y][++d[y]]=x;
 77     }
 78     
 79     for (int i=1;i<=n-1;i++)
 80     {
 81         a[i][i]=1;
 82         for (int j=1;j<=d[i];j++)
 83         {
 84             int k=t[i][j];
 85             if (k==n) continue;
 86             a[i][k]-=1.0/d[k];
 87         }
 88     }
 89     n--;
 90     a[1][n+1]=1;
 91     //debug();
 92     gauss(n);
 93     for (int i=1;i<=m;i++) w[i]=v[e[i].s]/(d[e[i].s]*1.0)+v[e[i].t]/(d[e[i].t]*1.0);
 94     sort(w+1,w+m+1,cmp);
 95     lb ans=0;
 96     for (int i=1;i<=m;i++)
 97         ans+=w[i]*i;
 98     printf("%.3lf\n",ans);
 99     return 0;
100 }

 

转载于:https://www.cnblogs.com/zig-zag/archive/2013/05/07/3065987.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值