[bzoj3143][Hnoi2013]游走——动态规划+高斯消元
题目大意:
一个无向连通图,顶点从1编号到N,边从1编号到M。
小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。
思路:
由于我们需要确定每条边的权值,所以不妨将每条边期望意义下需要走的次数给求出来。
先考虑每条边单独考虑的时候要怎么做,设\(f_u\)表示考虑第\(j\)条边的时候从\(u\)出发期望意义下还要走多少次\(j\)边,不难发现直接动态规划状态转移会有环,于是可以对于每一个点都列一个方程组,\(n^3\)高斯消元解出1号点的答案即可。
但是每条边单独考虑的话,最多可能有\(n^2\)条边,那么时间复杂度就达到了\(n^5\),显然不是这样做。
考虑将边的问题转化到点上面,我们求出每个点期望意义下经过多少次,那么每条边\((u,v)\)期望意义下经过的次数就是\(\frac{f_u}{d_u}+\frac{f_v}{d_v}\),这样以后我们就只需要做一次\(n^3\)的高斯消元了。
设\(f_{u}\)为点\(u\)期望意义下需要经过多少次:
\[f_{u}=\sum_{v,v\not=n}\frac{f_{v}}{d_v}
\]
当然1号点和\(n\)号点需要特殊考虑。
/*=======================================
* Author : ylsoi
* Time : 2019.2.11
* Problem : bzoj3143
* E-mail : ylsoi@foxmail.com
* ====================================*/
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;
using namespace std;
void File(){
freopen("bzoj3143.in","r",stdin);
freopen("bzoj3143.out","w",stdout);
}
template<typename T>void read(T &_){
_=0; T f=1; char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
_*=f;
}
const int maxn=500+10;
const double eps=1e-7;
int n,m,d[maxn];
int beg[maxn],to[maxn*maxn*2],las[maxn*maxn*2],cnte=1;
double f[maxn][maxn],p[maxn],e[maxn*maxn],ans;
void add(int u,int v){
las[++cnte]=beg[u],beg[u]=cnte,to[cnte]=v;
las[++cnte]=beg[v],beg[v]=cnte,to[cnte]=u;
}
void print(){
REP(i,1,n){
REP(j,1,n+1)printf("%lf ",f[i][j]);
cout<<endl;
}
cout<<"====================================="<<endl;
}
void Gauss(){
//print();
REP(i,1,n-1){
REP(j,i+1,n)if(fabs(f[j][i])>fabs(f[i][i]))swap(f[i],f[j]);
REP(j,i+1,n){
double t=f[j][i]/f[i][i];
REP(k,i,n+1)f[j][k]=f[j][k]-f[i][k]*t;
}
//print();
}
DREP(i,n,1){
p[i]=f[i][n+1]/f[i][i];
REP(j,1,i-1)f[j][n+1]-=f[j][i]*p[i];
}
//REP(i,1,n)cout<<p[i]<<endl;
//cout<<"================================"<<endl;
REP(i,1,m){
int u=to[i<<1],v=to[i<<1|1];
e[i]=p[u]/d[u]*(u!=n)+p[v]/d[v]*(v!=n);
//cout<<u<<" "<<v<<" "<<e[i]<<endl;
}
}
int main(){
//File();
read(n),read(m);
int u,v;
REP(i,1,m)read(u),read(v),add(u,v),++d[u],++d[v];
REP(i,1,n){
f[i][i]=1;
if(i==1 || i==n)f[i][n+1]=1;
if(i==n)continue;
for(int j=beg[i];j;j=las[j]){
v=to[j];
if(v==n)continue;
f[i][v]=-1.0/d[v];
}
}
Gauss();
sort(e+1,e+m+1);
REP(i,1,m)ans+=e[i]*(m-i+1);
printf("%.3lf\n",ans);
return 0;
}