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
",sum);
return 0;
}