题目
Vjudge
一个无向连通图,顶点从1编号到N,边从1编号到M。
小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。
无向图
思路
考虑
其中
为第
条边经过期望次数
显然
大的配
小的
如果我们的边拆点的话点太多了,因为这种有依赖性的
一般要用高斯消元
考虑
的期望可以利用期望的线性性使得
,其中 为经过点的期望次数
转化为求点的经过期望次数
根据期望的线性性列出状态转移方程:
会加1是因为一来就在
根据
的变形可以推出
(可理解为随机游走时候一碰到
点就消失了)
变形后高斯消元即可
代码
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstring>
#include<climits>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
//#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
//char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
inline int read() {
bool f=0;int x=0;char c=getchar();
while(c<'0'||'9'<c){if(c==EOF)exit(0);if(c=='-')f=1;c=getchar();}
while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return !f?x:-x;
}
#define MAXN 500
#define INF 0x3f3f3f3f
int de[MAXN+5];
vector<int> G[MAXN+5];
double a[MAXN+5][MAXN+5];
double Abs(double x){return x>0?x:-x;}
void Print(int n){
for(int i=1;i<=n;i++,puts(""))
for(int j=1;j<=n+1;j++)
printf("%f ",a[i][j]);
return ;
}
void Guass(int n){
//Print(n);
for(int i=1;i<=n;i++){
int r=i;
for(int j=i;j<=n;j++)
if(Abs(a[r][i])<Abs(a[j][i]))
r=j;
swap(a[r],a[i]);
for(int j=1;j<=n;j++)
if(j!=i){
double tmp=a[j][i]/a[i][i];
a[j][i]=0;
for(int k=i+1;k<=n+1;k++)
a[j][k]-=tmp*a[i][k];
}
//Print(n);
}
for(int i=1;i<=n;i++)
a[i][n+1]/=a[i][i];
//Print(n);
return ;
}
double E[MAXN*MAXN+5];
int U[MAXN*MAXN+5],V[MAXN*MAXN+5];
int main(){
int n=read(),m=read();
for(int i=1;i<=m;i++){
U[i]=read(),V[i]=read();
de[U[i]]++,de[V[i]]++;
G[U[i]].push_back(V[i]),G[V[i]].push_back(U[i]);
}
for(int u=1;u<n;u++){
a[u][u]=1;
for(int i=0;i<(int)G[u].size();i++){
int v=G[u][i];
if(v!=n)
a[u][v]=-1.0/de[v];
}
}
a[1][n]=1;
Guass(n-1);
for(int i=1;i<=m;i++)
E[i]=a[U[i]][n]/de[U[i]]+a[V[i]][n]/de[V[i]];
sort(E+1,E+m+1);
double ans=0;
for(int i=1;i<=m;i++)
ans+=(m-i+1)*E[i];
printf("%.3lf\n",ans);
return 0;
}