题目描述:
一个有向无环图,边都是从编号小的点连向编号大的点,1为起点,n为终点,1~n-1每个点至少有一条出边,从1号点每次随机选一条边走,直到走到n号点,所经过的期望边数记为
E
E
E,现在你可以断掉一条边(边的起点的出边大于1条),最小化
E
E
E
题目分析:
首先一个dfs求出
f
[
i
]
f[i]
f[i]表示
i
i
i走到n的期望边数。
f
[
u
]
=
1
+
1
d
e
g
∑
v
f
[
v
]
f[u]=1+\frac 1{deg}\sum_vf[v]
f[u]=1+deg1v∑f[v]
官方题解是一个点的所有出边中只可能删去
f
[
v
]
f[v]
f[v]最大的那条边,所以枚举删哪个点的边,然后重新DP,复杂度
O
(
n
m
)
O(nm)
O(nm)
但是重新DP显然太浪费,断掉一条边相当于修改起点的
f
f
f值,从而对
f
[
1
]
f[1]
f[1]产生影响。
由
g
[
v
]
=
1
d
e
g
∑
v
g
[
u
]
g[v]=\frac 1{deg}\sum_vg[u]
g[v]=deg1∑vg[u]我们就可以算出1号点到
x
x
x的概率(说概率也许不太恰当,因为没有比较,本质上就是转移的总系数),那么
g
[
x
]
∗
Δ
f
[
x
]
g[x]*\Delta f[x]
g[x]∗Δf[x]就是它对1号点的贡献了。
计算 g g g可以直接编号由小到大枚举转移,dfs也可以编号由大到小枚举转移(这道题的边比较特殊)。
我在计算
g
g
g的时候用的是拓扑排序,但是我一开始的栈中只放入了1号点(因为想着只有g[1]=1,其它没有入度的点都没有意义),这就导致路径中的一些点受到其它没有入度的点的限制而无法转移。痛失AK。。QWQ。。惨啊
Code:
#include<bits/stdc++.h>
#define maxn 605
#define maxm maxn*maxn/2
using namespace std;
int n,m,deg[maxn],stk[maxn],top;
double f[maxn],g[maxn],ans;
vector<int>G[maxn];
double dfs(int u){
if(u==n) return 0;
if(f[u]) return f[u];
for(int v: G[u]) f[u]+=dfs(v);
return f[u]=f[u]/G[u].size()+1;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1,x,y;i<=m;i++) scanf("%d%d",&x,&y),G[x].push_back(y),deg[y]++;
dfs(1);
g[1]=1;
for(int i=1;i<=n;i++) if(!deg[i]) stk[++top]=i;
while(top){
int u=stk[top--]; if(u==n) break;
for(int v: G[u]){
g[v]+=g[u]/G[u].size();
if(!--deg[v]) stk[++top]=v;
}
}
for(int u=1;u<n;u++) if(G[u].size()>1){
double tmp=0; for(int v: G[u]) tmp+=f[v];
for(int v: G[u])
ans=max(ans,g[u]*(f[u]-((tmp-f[v])/(G[u].size()-1)+1)));
}
printf("%.10f\n",f[1]-ans);
}