bzoj3470 Freda’s Walk
原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=3470
题意:
有向无环图,求从点0出发,走到一个出度为0的点停止,点u出发的所有边的权值之和为s,从u到v的边的权值为w,走向v的概率就是 w/s。可以任意删一条边(也可不删边),求最长期望路径长度。
因为是权限题,贴一下输入输出
Input
第一行两个正整数 n、m。
接下来m行每行三个整数u、v、w,表示从u到v有一条无向边,边权w。
Output
输出路程长度的最大期望值,四舍五入保留六位小数。
Sample Input
4 5
0 1 2
0 2 1
0 3 3
1 3 1
2 3 4
Sample Output
2.000000
数据范围
对于 100% 的数据,2<=n<=10000,1<=m<=100000,0<=u,v< n
题解:
这题我最开始想的就是dp[u][0/1] 表示按拓扑序其之后的边 没删过/删过 的,从这个点到停止的期望路径长度。每个dp[u][1]只能取一个dp[v][1]转移过来。答案即在max(f[1][0],f[1][1])
但是,这样是错的。
这里有一个反例:
如果删3—>4这条边 ,当计算f[1]时,对点2,点3,都是 ‘删过’,不能说只取一个f[v][1]。
现在我们不要f[i][0/1]了,定义f[i]为点到停止的期望路径长度(相当于原来的f[i][0])。
为了避免上面的情况,之间先计算出从1点到每个点的概率p[i],则不删边时这个点对于答案的贡献就是p[i]*f[i]。
枚举每个点所有的出边,计算出删除这个点某条出边所得的最大的新期望 f[i]’。对于答案增加的贡献就是 ( f[i]’-f[i] ) * p[i]
所以 ans=f[1]+max( ( f[i]’-f[i] ) * p[i] )
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#define LL long long
using namespace std;
const int N=10005;
const int M=100005;
int n,m,head[N],to[M],nxt[M],out[N],in[N],w[M],num=0;
int U[M],V[M],W[M];
double f[N][2],p[N],mx;
bool vis[N];
void build(int u,int v,int ww)
{
num++;
to[num]=v;
nxt[num]=head[u];
w[num]=ww;
head[u]=num;
}
void getp()
{
queue<int> Q;
for(int i=1;i<=n;i++)if(!in[i]) Q.push(i);
while(!Q.empty())
{
int u=Q.front(); Q.pop();
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
p[v]+=p[u]*(double)w[i]/out[u];
in[v]--;
if(in[v]==0)
Q.push(v);
}
}
}
void dfs(int u)
{
vis[u]=1;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(!vis[v]) dfs(v);
if(out[u]) f[u][0]+=(f[v][0]+1)*(double)w[i];
}
double sum=f[u][0];
if(out[u]) f[u][0]=(double)f[u][0]/out[u];
if(!out[u]) return;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(out[u]-w[i]>0) f[u][1]=max(f[u][1],(double)(sum-(f[v][0]+1)*(double)w[i])/(out[u]-w[i]));
}
mx=max(mx,(f[u][1]-f[u][0])*p[u]);
}
int main()
{
freopen("secretbase.in","r",stdin);
freopen("secretbase.out","w",stdout);
scanf("%d%d",&n,&m);
mx=0.0;
for(int i=1;i<=m;i++)
{
int u,v,ww;
scanf("%d%d%d",&u,&v,&ww);
u++,v++; in[v]++;
out[u]+=ww;
U[i]=u; V[i]=v; W[i]=ww;
build(u,v,ww);
}
double ans=0;
for(int i=1;i<=n;i++)
{
vis[i]=0; p[i]=0.0;
}
p[1]=1.0;
getp();
for(int i=1;i<=n;i++)
{
vis[i]=0; f[i][0]=f[i][1]=0.0;
}
dfs(1);
printf("%0.6lf\n",f[1][0]+mx);
return 0;
}