• 用Dij的思想优化DP


    一、内容

    如果 (DP) 的状态转移方程为 (f[i]=min{f[i],sum f[j]+k})

    那么我们就可以考虑用 (Dij) 的思想去优化它

    因为如果某个点的 (f) 值是最小的,那么就没有其它的点可以影响它

    因此我们每一次从堆中取出最小的点对其它点进行更新即可

    二、例题

    1、洛谷P4745 [CERC2017]Gambling Guide

    题目描述

    传送门

    分析

    按照期望题一般的做法,我们设 (f[u]) 为从 (u) 走到终点 (n) 的期望花费

    (du[u]) 为节点 (u) 的出度

    那么 (f[u]= frac{sum_{u->v} min(f[u],f[v])}{du[u]}+1)

    我们会发现这个式子既包含 (f[u]) 本身又包含与 (f[u]) 相邻的点 (f[v])

    不好直接转移

    但是我们可以确定,如果 (f[v]<f[u]) ,那么 (f[v]) 一定会对 (f[u]) 做出贡献

    因此,我们可以利用 (Dij) 的思想开一个小根堆,每次取出最小的来更新其它的

    因为当前的值是最小的,所以一定不会有其它的点可以影响它

    我们设 (u) 被与它相邻的点的 (f) 值更新了 (cnt) 次,这些值的和为 (sum)

    (f[u]=frac{sum+(du[u]-cnt[u]) imes f[u]}{du[u]}+1)

    整理可得 (f[u]=frac{sum+du[u]}{cnt})

    代码

    #include<cstdio>
    #include<cstring>
    #include<queue>
    const int maxn=6e5+5;
    inline int read(){
    	int x=0,fh=1;
    	char ch=getchar();
    	while(ch<'0' || ch>'9'){
    		if(ch=='-') fh=-1;
    		ch=getchar();
    	}
    	while(ch>='0' && ch<='9'){
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return x*fh;
    }
    int head[maxn],tot=1;
    struct asd{
    	int to,next;
    }b[maxn];
    void ad(int aa,int bb){
    	b[tot].to=bb;
    	b[tot].next=head[aa];
    	head[aa]=tot++;
    }
    struct jie{
    	int num;
    	double jl;
    	jie(){}
    	jie(int aa,double bb){
    		num=aa,jl=bb;
    	}
    	bool operator < (const jie &A) const{
    		return jl>A.jl;
    	}
    };
    int n,m,du[maxn],cnt[maxn];
    double sum[maxn],f[maxn];
    bool vis[maxn];
    std::priority_queue<jie> q;
    void dij(){
    	q.push(jie(n,0));
    	while(!q.empty()){
    		int now=q.top().num;
    		q.pop();
    		if(vis[now]) continue;
    		vis[now]=1;
    		for(int i=head[now];i!=-1;i=b[i].next){
    			int u=b[i].to;
    			if(vis[u]) continue;
    			cnt[u]++;
    			sum[u]+=f[now];
    			f[u]=(du[u]+sum[u])/cnt[u];
    			q.push(jie(u,f[u]));
    		}
    	}
    }
    int main(){
    	memset(head,-1,sizeof(head));
    	n=read(),m=read();
    	for(int i=1;i<=m;i++){
    		int aa,bb;
    		aa=read(),bb=read();
    		ad(aa,bb);
    		ad(bb,aa);
    		du[aa]++,du[bb]++;
    	}
    	dij();
    	printf("%.10f
    ",f[1]);
    	return 0;
    }
    
    

    2、李青(内部题)

    题目描述

    传送门

    分析

    我们设消灭第 (i) 个头的代价是 (f[i])

    则,(f[i]=min(f[i],a[i]+sum f[j],b[i]))

    同样地,我们在一开始把所有的 (f) 值扔进小根堆里

    每次取出队首的元素来更新其它值

    代码

    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<vector>
    #include<iostream>
    #include<cstdlib>
    const int maxn=6e5+5;
    typedef long long ll;
    int head[maxn],tot=1;
    struct asd{
    	int to,next;
    }b[maxn];
    int n;
    void ad(int aa,int bb){
    	b[tot].to=bb;
    	b[tot].next=head[aa];
    	head[aa]=tot++;
    }
    struct jie{
    	int num;
    	ll jl;
    	jie(){}
    	jie(int aa,ll bb){
    		num=aa,jl=bb;
    	}
    	bool operator < (const jie &A) const{
    		return jl>A.jl;
    	}
    };
    ll a[maxn],f[maxn];
    int k[maxn];
    bool vis[maxn];
    std::priority_queue<jie> q;
    void dij(){
    	while(!q.empty()){
    		int now=q.top().num;
    		q.pop();
    		if(vis[now]) continue;
    		vis[now]=1;
    		if(k[now]==0) f[now]=std::min(f[now],a[now]);
    		for(int i=head[now];i!=-1;i=b[i].next){
    			int u=b[i].to;
    			if(!vis[u]){
    				a[u]+=f[now];
    				k[u]--;
    				if(k[u]==0) q.push(jie(u,a[u]));
    			}
    		}
    	}
    }
    int main(){
    	memset(head,-1,sizeof(head));
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%lld%lld%d",&a[i],&f[i],&k[i]);
    		q.push(jie(i,f[i]));
    		for(int j=1;j<=k[i];j++){
    			int aa;
    			scanf("%d",&aa);
    			ad(aa,i);
    		}
    	}
    	dij();
    	long long ans=0;
    	for(int i=1;i<=n;i++){
    		ans+=f[i];
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    常见面试题
    3*0.1 == 0.3 将会返回什么?true 还是 false?
    poj_2186 强连通分支
    强连通分量、割点、桥
    最小生成树
    poj_2349 Kruskal 最小生成树
    poj_1258 prim最小生成树
    最短路径
    poj_1125 Floyd最短路
    poj_1860 SPFA
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/13746686.html
Copyright © 2020-2023  润新知