题意:给定n个点m条边。每条边的权值还没决定,权值大小为从1到m。从1出发每次等概率选一条出边向下走,直到走到n点停止,路径代价就是边权总和。由你来决定边权来使得上诉路径代价期望值最小。
解法:点这么少边这么多,比较容易想到我们先求出点的期望经过次数dp[i],那么对于一条边(u,v)它的期望经过次数就等于 (dp[u]/du[u]+dp[v]/du[v])。对于这个值排序之后贪心赋予边权大小即可。
问题在于怎么得到每个点的期望经过次数呢?
这里直接写状态转移方程 dp[u]=sigma(dp[v]/du[v]) + [u==1];(v表示出点,du表示度数)
特别要注意几个点:
①为什么是du[v]而不是du[u]?因为此时算的是从1出发的 。②为什么u==1时候才加一,其实这个问题结果和上一个一样。 ③注意终点n不参与上诉的转移,为什么? 因为一旦到达终点n之后就不再游走了。
代码如下:
#include<bits/stdc++.h> using namespace std; const int N=500+10; const double eps=1e-6; int n,m,k; vector<int> G[N]; struct edge{ int x,y; double gx=0; bool operator < (const edge &rhs) const { return gx>rhs.gx; } }e[N*N]; double c[N][N],b[N]; void Gauss() { int r=0; for (int i=1;i<=n;i++) { int j=r+1; while (j<=m && fabs(c[j][i])<eps) j++; //从下面方程找一个第i位不为0的 if (j==m+1) continue; //不存在第i位不为0的方程 r++; //矩阵的秩 for (int k=1;k<=n;k++) swap(c[r][k],c[j][k]); //存在第i位不为0的方程,交换上去 swap(b[r],b[j]); for (int j=1;j<=m;j++) { //以r方程回代m个方程 if (r==j) continue; double rate=c[j][i]/c[r][i]; for (int k=i;k<=n;k++) c[j][k]-=c[r][k]*rate; b[j]-=b[r]*rate; } } // for (int i=1;i<=m;i++) if (b[i]) { //判断无解情况 // bool ok=0; // for (int j=1;j<=n;j++) if (c[i][j]) { ok=1; break; } // if (!ok) { puts("Inconsistent data."); return; } // } //if (r<n) { puts("Multiple solutions."); return; } //有解但多解 for (int i=1;i<=n;i++) b[i]=b[i]/c[i][i]; //唯一解求解 } int main() { cin>>n>>k; m=n; for (int i=1;i<=k;i++) { int x,y; scanf("%d%d",&x,&y); G[x].push_back(y); G[y].push_back(x); e[i].x=x; e[i].y=y; e[i].gx=0; } memset(c,0,sizeof(c)); memset(b,0,sizeof(b)); for (int i=1;i<=n;i++) { c[i][i]+=1.0; if (i==n) break; for (int j=0;j<G[i].size();j++) { int v=G[i][j]; if (v==n) continue; c[i][v]-=1.0/G[v].size(); } if (i==1) b[i]+=1.0; } Gauss(); for (int i=1;i<=k;i++) { e[i].gx+=b[e[i].x]/G[e[i].x].size(); e[i].gx+=b[e[i].y]/G[e[i].y].size(); } sort(e+1,e+k+1); double ans=0; for (int i=1;i<=k;i++) ans+=e[i].gx*i; printf("%.3lf\n",ans); return 0; }