[省选练习]P

[省选练习]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\n",dis[1]);
	return 0;
}

/*
4 4
1 2
2 4
1 3
3 4

6.0000000000
*/

posted on 2018-12-25 21:43  ErkkiErkko  阅读(109)  评论(0编辑  收藏  举报