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
2 3
1 2
1 3
Sample Output
HINT
Source
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~高斯消元+贪心+神奇的思路+期望~
首先,我们可以用贪心来求解,因为边权对每条边的期望行走次数无影响,所以我们把经常经过的边标为小号,反之标为大号。
这道题里面的图可能有环,所以一般的期望DP是不行的。我们可以列出期望之间的方程来求解。
但是注意到边又多又不容易找关系,所以我们以点为依据列方程。用a[i]表示i点的经过次数期望值,那么容易列出方程a[i]=a[1]*k[1][i]+a[1]*k[2][i]+…+a[n-1]*k[n-1][i](没有i),其中k[i][j]表示从i点走到j点的概率(预处理得出,后面写求它的方法),注意其中没有n点,因为走到n点就结束;而且i==1时要多一个常数项1,因为1点在开始的时候就已经走过一次。这样,我们把所有含未知数的项移到等式右面,就成了n-1个方程,n-1个未知数,显然有唯一解,用高斯消元就可以了~
预处理出每个点的度数du[i]。
k[i][j](从i点走到j点的概率)的求法:i、j不相连时为0,相连时k[i][j]=1.0/du[i]。
最后,已知所有点的期望值,则一条边(x,y)的期望经过次数为val[i]=a[x]/du[x]+a[y]/du[y]。
按照上面的写就好了~
注意:所有1都要写成1.0!不然会WA得很惨!(知道真相的我眼泪掉下来)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,du[501];
double k[501][501],a[501][501],val[125001],ans;
bool b[501][501];
struct node{
int x,y;
}aa[125001];
int read()
{
int totnum=0;char ch=getchar();
while(ch<'0' || ch>'9') ch=getchar();
while(ch>='0' && ch<='9') {totnum=(totnum<<1)+(totnum<<3)+ch-'0';ch=getchar();}
return totnum;
}
void guass()
{
for(int i=1;i<n;i++)
{
for(int j=i+1;j<=n+1;j++) a[i][j]/=a[i][i];a[i][i]=1;
for(int j=i+1;j<=n;j++)
{
for(int k=i+1;k<=n+1;k++) a[j][k]-=a[j][i]*a[i][k];
a[j][i]=0;
}
}
for(int i=n;i>1;i--)
{
for(int j=i+1;j<=n+1;j++) a[i][j]/=a[i][i];a[i][i]=1;
for(int j=i-1;j;j--)
{
for(int k=i+1;k<=n+1;k++) a[j][k]-=a[j][i]*a[i][k];
a[j][i]=0;
}
}
}
int main()
{
n=read();m=read();
for(int i=1;i<=m;i++)
{
aa[i].x=read();aa[i].y=read();
du[aa[i].x]++;du[aa[i].y]++;b[aa[i].x][aa[i].y]=b[aa[i].y][aa[i].x]=1;
}
n--;a[1][n+1]=-1.0;
for(int i=1;i<n;i++)
for(int j=i+1;j<=n;j++) if(b[i][j]) k[i][j]=1.0/du[i],k[j][i]=1.0/du[j];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i==j) a[i][j]=-1.0;
else a[i][j]=k[j][i];
guass();
for(int i=1;i<=m;i++) val[i]=a[aa[i].x][n+1]/du[aa[i].x]+a[aa[i].y][n+1]/du[aa[i].y];
sort(val+1,val+m+1);
for(int i=1;i<=m;i++) ans+=val[m-i+1]*(double)i;
printf("%.3lf\n",ans);
return 0;
}