bzoj 3143: [Hnoi2013]游走
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
2 3
1 2
1 3
Sample Output
3.333
HINT
边(1,2)编号为1,边(1,3)编号2,边(2,3)编号为3。
Source
实在是闲的没事做才把这个题做掉。。。
首先我们可以需要计算每条边被经过的概率,因为要总期望最小,那么要让经过概率高的边的权值小,sort一遍即可。。。
如何求一条边被经过的概率呢,设边(x,y),经过x的概率是g[x],经过y的概率是g[y],x的度数为du[x],y的度数为du[y]。。。
那么答案显然等于g[x]/du[x]+g[y]/du[x];
然后我们相当于是要求经过每个点的概率(因为到了n就停止,所以我们要求经过1-n-1的点的概率,经过n的概率为0)
那么显然g[x]=∑g[y]/du[y]。。。
然后我们发现这是一个转移有环的dp,我们可以通过高斯消元来解决,经过1的概率为1。。
然后得出解,那么再算出每条边经过的概率,然后sort一遍出解。。。
// MADE BY QT666 #include<cstdio> #include<algorithm> #include<cmath> #include<iostream> #include<cstring> using namespace std; typedef long long ll; const int N=3000; int n,m,head[N],nxt[300050],to[300050],cnt,du[N]; double a[N][N],v[300050]; void gauss() { for(int i=1;i<=n;i++) { int t=i; while(!a[t][i]) t++; if(i!=t) swap(a[t],a[i]); double k=a[i][i]; for(int j=i;j<=n+1;j++) a[i][j]/=k; for(int j=1;j<=n;j++) if(j!=i&&a[j][i]) { k=a[j][i]; for(int p=i;p<=n+1;p++) a[j][p]-=k*a[i][p]; } } } void lnk(int x,int y){ du[x]++;du[y]++; to[++cnt]=y,nxt[cnt]=head[x],head[x]=cnt; to[++cnt]=x,nxt[cnt]=head[y],head[y]=cnt; } struct data{ int x,y; }e[300050]; bool cmp(double a,double b){return a>b;} int main(){ freopen("walk.in","r",stdin); freopen("walk.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){scanf("%d%d",&e[i].x,&e[i].y);lnk(e[i].x,e[i].y);} n--; for(int x=1;x<=n;x++){ for(int i=head[x];i;i=nxt[i]){ int y=to[i];if(y!=n+1) a[x][y]=1.0/du[y]; } a[x][x]=-1.0; } a[1][n+1]=-1.0;gauss(); for(int i=1;i<=m;i++) v[i]=a[e[i].x][n+1]/du[e[i].x]+a[e[i].y][n+1]/du[e[i].y]; sort(v+1,v+1+m,cmp);double ans=0;for(int i=1;i<=m;i++) ans+=v[i]*i;printf("%.3f\n",ans); return 0; }