zoukankan      html  css  js  c++  java
  • $Luogu$ $P4745$ $[CERC2017]$ $Gambling$ $Guide$

    链接

    背景

    (Central) (Europe) (Regional) (Contest) (G) 题, (Luogu) (P4745/BZOJ5197/Gym101620G)(Google) (Chrome) 与原题面更配哦!)

    题意

    给定一张 (n) 个点, (m) 条边的无边权的无向图。有一人从 (1) 号点出发,可以随机向一个和当前直接相连的点走去,花费 (1) 的代价;也可以不动,重新随机一个点,也花费 (1) 的代价。求到达 (n) 点时的最小总花费。答案四舍五入保留 (6) 位小数。

    解法

    根据之前总结过的期望dp的设计方法,因为只有一个终点,且状态已知(期望花费为 (0) ),因此考虑逆推。自然地,设 (f_x) 表示点 (x) 到终点的期望花费。用 (E) 表示边集, (deg_x) 表示 (x) 点的度数,则有 (f_x=frac{ sum_limits{(x,y) in E} min { f_x,f_y } }{deg_x}+1)
    那么,对于一个点 (x) 来说,能对它的期望产生贡献的相邻的点 (y) 必然有 (f_y<f_x) 。一开始不妨令所有的 (x) 点在计算 $ min { f_x,f_y }$ 时都为 (f_x) (下文会证明确实只会出现这种情况)。
    假设这样的 (y) 点有 (cnt_x) 个,则有 (f_x=frac{ sum_limits{(x,y) in E,f_y<f_x} f_y}{deg_x}+ frac{(deg_x-cnt_x) imes f_x}{deg_x}+1=frac{ sum_limits{(x,y) in E,f_y<f_x} f_y}{deg_x}+(1- frac{cnt_x}{deg_x} ) imes f_x+1)
    因此,果断求出 $ sum_limits{(x,y) in E,f_y<f_x} f_y $ ,记为 (sum_x) ,则 (f_x=frac{sum_x}{deg_x}+(1- frac{cnt_x}{deg_x} ) imes f_x+1) ,化简得 (f_x= frac{deg_x+sum_x}{cnt_x})
    也就是说,只要相连的 (x,y) 两点满足 (f_y<f_x) ,我们就可以利用 (f_y) 来更新 (f_x) 的值。是不是很像堆优化的 (dijkstra) 的松弛操作呢?时间复杂度与堆优化的 (dijkstra) 相同,为 (O((m+n)logn))

    (trick)

    (1.) 利用 (f_y) 来更新 (f_x) 的值的算法正确性证明:
    设更新后的 (x) 点的 (f) 值为 (f_x')(f_x-f_y=Delta) (显然 (Delta>0) )。由上文推出的式子得 (f_x= frac{deg_x+sum_x}{cnt_x}) ,更新后有 (f_x'= frac{deg_x+sum_x+f_y}{cnt_x+1}) ,两者相减并化简得 (f_x-f_x'=frac{Delta}{cnt_x+1}) ,则 (f_x-f_x'=frac{f_x-f_y}{cnt_x+1}) ,即 (0<f_x-f_x'<f_x-f_y) ,也即 (f_x>f_x'>f_y)
    因此每一次松弛操作不会使得 (f_x) 变大,也不会使得其小于 (f_y) 。证毕。

    (2.) 一个非常用不经典套路:对于确定的初始状态仅有极少数(大多数时候仅有一个),其余的状态与相邻的部分(大多数时候是相邻的点)有关的dp,考虑(放到图上)以最短路的方式进行转移。

    细节

    (1.) 请注意堆的第一维数据类型是 (double)

    (2.) 请注意本题是无向图,要双向建边、双向记度数。

    (3.) 请特别注意标记数组的使用和判断:每个点在自己更新完所有的点后就不能再被更新(因为此时已经是能达到的最小的值了)。

    代码

    $View$ $Code$
    
    //省略头文件
    using namespace std;
    inline int read()
    {
    	int ret=0,f=1;
    	char ch=getchar();
    	while(ch>'9'||ch<'0')
    	{
    		if(ch=='-')
    			f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    	{
    		ret=(ret<<1)+(ret<<3)+ch-'0';
    		ch=getchar();
    	}
    	return ret*f;
    }
    int n,m,u,v,w;
    int num,head[600005],deg[300005],cnt[300005];
    double f[300005],sum[300005];
    bool vis[300005];
    priority_queue > q;
    struct edge
    {
    	int ver,nxt;
    }e[600005];
    inline void adde(int u,int v)
    {
    	e[++num].ver=v;
    	e[num].nxt=head[u];
    	head[u]=num;
    }
    inline void dijkstra()
    {
    	q.push(make_pair(0,n));
    	while(!q.empty())
    	{
    		int x=q.top().second;
    		q.pop();
    		if(vis[x])
    			continue;
    		vis[x]=1;
    		for(register int i=head[x];i;i=e[i].nxt)
    		{
    			int y=e[i].ver;
    			if(vis[y])
    				continue;
    			cnt[y]++;
    			sum[y]+=f[x];
    			f[y]=(deg[y]+sum[y])/cnt[y];
    			q.push(make_pair(-f[y],y));
    		}
    	}
    }
    int main()
    {
    	n=read();
    	m=read();
    	for(register int i=1;i<=m;i++)
    	{
    		u=read();
    		v=read();
    		adde(u,v);
    		adde(v,u);
    		deg[u]++;
    		deg[v]++;
    	}
    	dijkstra();
    	printf("%0.7lf
    ",f[1]);
    	return 0;
    }
    
  • 相关阅读:
    阿里Java完整学习资料
    Android 矢量图详解
    关于 Android 状态栏的适配总结
    SSM框架视频资料
    hideSoftInputFromWindow
    微信支付
    git+coding.net记录篇
    ClassNotFoundException超限
    react native初步常见问题
    React Native windows搭建记录
  • 原文地址:https://www.cnblogs.com/Peter0701/p/11741788.html
Copyright © 2011-2022 走看看