[省选练习]Path:期望DP+Dijkstra
题意
(n,m leq 10^5)
分析
很有趣的一道题。
记(dis[x])为从(x)到(n)的期望时间。
题目中要求的最优策略,实际上就是只从(dis)大的地方向(dis)小的地方移动。
所以,对于两个相邻的结点(x,y),(dis[x])能更新(dis[y])当且仅当(dis[x]<dis[y])。
我们希望所有更新(dis[x])的(dis[y])从小到大,这样我们就能及时停止所有会使答案变差的更新。
被确定的(dis[x])从小到大,很类似于Dijkstra算法的工作原理。
类比优先队列优化的Dijkstra算法,我们每次取出队首元素,队首元素的(dis)此时被确定,不会再被其他结点更新,且每次取出的队首的(dis)一定单调不降。我们用取出的队首尝试更新其他未被确定的结点的(dis),并将更新成功的结点插入到优先队列中(事实上这里一定更新成功)。
如何更新?
有:
[dis[x]=frac{m}{cnt[x]}+frac{sum[x]}{cnt[x]}
]
更新一下(cnt[x])和(sum[x]),通过上面的式子算出(dis[x])就好了。
考场上把Dijkstra里的continue;
敲成return;
,华丽爆零。
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <vector>
#include <queue>
#define rin(i,a,b) for(int i=(a);i<=(b);i++)
#define rec(i,a,b) for(int i=(a);i>=(b);i--)
#define trav(i,a) for(int i=head[(a)];i;i=e[i].nxt)
typedef long long LL;
using std::cin;
using std::cout;
using std::endl;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
const int MAXN=100005;
int n,m,ecnt,head[MAXN],cnt[MAXN];
double dis[MAXN],sum[MAXN];
bool vis[MAXN];
struct Edge{
int to,nxt;
}e[MAXN<<1];
struct Pair{
int pos;
double dis;
inline friend bool operator > (Pair x,Pair y){
return x.dis>y.dis;
}
};
std::priority_queue<Pair,std::vector<Pair>,std::greater<Pair> > q;
inline void add_edge(int bg,int ed){
ecnt++;
e[ecnt].to=ed;
e[ecnt].nxt=head[bg];
head[bg]=ecnt;
}
inline void dijkstra(){
rin(i,1,n) dis[i]=1e12;
while(!q.empty()) q.pop();
dis[n]=0;
q.push((Pair){n,0});
while(!q.empty()){
int x=q.top().pos;q.pop();
if(vis[x]) continue;
vis[x]=1;
trav(i,x){
int ver=e[i].to;
if(!vis[ver]&&dis[ver]>dis[x]){
sum[ver]+=dis[x];
cnt[ver]++;
dis[ver]=m*1.0/cnt[ver]+sum[ver]/cnt[ver];
q.push((Pair){ver,dis[ver]});
}
}
}
}
int main(){
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
n=read(),m=read();
rin(i,1,m){
int u=read(),v=read();
add_edge(u,v);
add_edge(v,u);
}
dijkstra();
printf("%.10lf
",dis[1]);
return 0;
}
/*
4 4
1 2
2 4
1 3
3 4
6.0000000000
*/