点此看题面
大致题意: 一个无向连通图,小 Z Z Z从 1 1 1号顶点出发,每次随机选择某条边走到下一个顶点,并将 a n s ans ans加上这条边的编号,走到 N N N号顶点时结束。请你对边进行编号,使总分期望值最小。
由于贪心的思想,我们肯定是给期望访问次数最大的边编号为 1 1 1,第二大的编号为 2 2 2,第三大的编号为 3 3 3,以此类推。
那么我们应该怎么求出边的期望呢?
由于边的期望可以由点的期望转化得来,因此只要求出了点的期望,就能求出边的期望。
那么怎么求出点的期望呢?
这时就需要用高斯消元了。
L i n k Link Link
高斯消元详见博客高斯消元入门
下面是一张无向图。
如果我们用 S i S_i Si来表示编号为 i i i的节点被经过的期望次数,那么显然:
S 1 = S 2 5 + S 3 3 + S 4 3 + S 5 2 + S 6 3 + 1 S_1=\frac{S_2}5+\frac{S_3}3+\frac{S_4}3+\frac{S_5}2+\frac{S_6}3+1 S1=5S2+3S3+3S4+2S5+3S6+1
即编号为 x x x的点的期望 S x = ∑ S i d e g i S_x= \sum \frac{S_i}{deg_i} Sx=∑degiSi,其中 i i i为与 x x x有边相连的节点。
像这样,我们可以将每一个点的期望都用其他点的期望来表示。
还是以 S 1 S_1 S1为例,我们可以将这个式子移项:
S 1 − S 2 5 − S 3 3 − S 4 3 − S 5 2 − S 6 3 = 1 S_1-\frac{S_2}5-\frac{S_3}3-\frac{S_4}3-\frac{S_5}2-\frac{S_6}3=1 S1−5S2−3S3−3S4−2S5−3S6=1
将每个式子都进行这样的转换之后,就可以通过高斯消元来求解出每一个 S i S_i Si。
其中要注意的是,每一个式子中 S n S_n Sn的系数皆为 0 0 0(因为游走在走到 n n n号节点时结束),且第 1 1 1个式子等号右边的值为 1 1 1(因为游走从 1 1 1号节点开始),而其他式子等号右边的值皆为 0 0 0。
接下来的问题是,如何通过点的期望求出边的期望。
设 E i E_i Ei表示编号为 i i i的边被经过的期望次数,且编号为 i i i的边连接的两个节点为 x i x_i xi和 y i y_i yi,由于期望的性质,我们可以得到:
E i = S x i d e g x i + S y i d e g y i E_i=\frac{S_{x_i}}{deg_{x_i}}+\frac{S_{y_i}}{deg_{y_i}} Ei=degxiSxi+degyiSyi
这样就可以轻松求出每条边的期望了。
然后,按照开头所述的贪心思想,就能轻松求解该题了。
#include
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define abs(x) ((x)<0?-(x):(x))
#define LL long long
#define ull unsigned long long
#define swap(x,y) (x^=y,y^=x,x^=y)
#define Fsize 100000
#define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
#define N 500
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].from=x,e[ee].to=y,++deg[x])
char Fin[Fsize],*FinNow=Fin,*FinEnd=Fin;
using namespace std;
const double eps=1e-15;
int n,m,ee=0,lnk[N+5],deg[N+5];
struct edge
{
int from,to,nxt;
double val;
}e[N*N+5];
inline bool cmp(edge x,edge y)
{
return x.val-y.val>eps;
}
struct Gauss//高斯消元
{
double a[N+5][N+5],s[N+5];
inline void GetDataA(int x,int y,double v) {a[x][y]+=v;}
inline void GetDataS(int x,double v) {s[x]=v;}
inline void FindLine(int x)
{
register int i=x,j;register double t;
while(fabs(a[i][x])<eps) ++i;
for(t=s[i],s[i]=s[x],s[x]=t,j=1;j<=n;++j) t=a[i][j],a[i][j]=a[x][j],a[x][j]=t;
}
inline double GetAns()
{
register int i,j,k;register double delta,ans=0;
for(i=1;i<n-1;++i)
{
FindLine(i);
for(j=i+1;j<n;++j) for(s[j]+=(delta=-a[j][i]/a[i][i])*s[i],k=1;k<=n;++k) a[j][k]+=delta*a[i][k];
}
for(i=n-1;i;--i)
for(s[i]/=a[i][i],j=i-1;j;--j) s[j]-=a[j][i]*s[i];
for(i=2;i<=ee;++(++i)) e[i].val=s[e[i].from]/deg[e[i].from]+s[e[i].to]/deg[e[i].to];
for(sort(e+1,e+ee+1,cmp),i=1;i<=ee;++i) ans+=e[i].val*i;
return ans;
}
}S;
inline void read(int &x)
{
x=0;static char ch;
while(!isdigit(ch=tc()));
while(x=(x<<3)+(x<<1)+ch-48,isdigit(ch=tc()));
}
int main()
{
register int i,j,x,y;
for(read(n),read(m),i=1;i<=m;++i) read(x),read(y),add(x,y),add(y,x);
for(i=1;i<n;++i) for(S.GetDataA(i,i,1),j=lnk[i];j;j=e[j].nxt) if(e[j].to^n) S.GetDataA(i,e[j].to,-1.0/deg[e[j].to]);//根据上面推导出的式子,初始化高斯消元的式子
return S.GetDataS(1,1),printf("%.3lf",S.GetAns()),0;//得出答案并输出
}