zoukankan      html  css  js  c++  java
  • k短路

    k短路

    好像是一个比较简单的东西

    对于 正权有向图(displaystyle G=(V,E),V={V_i}_{i=1}^nE={(u_i,v_i,w_i)}_{i=1}^m)

    (s)(t)的前(k)短路

    考虑建立反图(G'=(V,E')),容易( ext{Dijkstra})求得(t)的单源最短路(dis_i),并且建立一棵最短路树

    考虑(s ightarrow t)的最短路,一定是走了一些树边和非树边

    选择一条非树边((u,v,w))会使长度增加(w'=w-(dis_u-dis_v)),称(w')为额外长度

    考虑我们选择的非树边序列((u_i,v_i,w_i)),显然有:(u_{i+1})(v_i)在最短路树上的祖先

    考虑用搜索扩展的方式来遍历所有路径情况

    记录当前的节点(u),产生的额外长度(d),则每次的扩展可以归纳为

    1.从(u)所有祖先中取出边的集合(S_u)

    2.依次遍历(S_u)中的所有边((u_i,v_i,w'_i)),进入递归(u'=v_i,d'=d+w'_i)

    每次扩展会产生一个新的状态,且恰好可以遍历每一个状态一次

    [ ]

    然而,为了求出前(k)短路,我们必须按照答案从小到大遍历

    那么我们首先需要将集合(S_u)排序,从小到大遍历,其次要对于不同的递归情况按照大小扩展

    容易想到用一个堆维护扩展的顺序,为了保存遍历(S_u)集合的过程,记录一个指针(p)

    此时,用堆维护扩展的方法显然:

    1.取出堆顶状态((u,d,p))

    2.转移

    2-1.在当前递归栈中移动(pleftarrow p+1),改变(d)

    2-2.模拟上面,建立新的递归栈,同时令指针为(0)

    得到(u'=v_{u,p},d'=d+w'_{v,0},p'=0)

    如果暴力处理出(S_u),则预处理复杂度为(O(nmlog m)),状态扩展复杂度为(O(2klog k))

    用可持久化可并堆处理(S_u)(p)记录当前堆顶节点指针

    每次扩展(pleftarrow lson_p)或者(pleftarrow rson_p),增加一个扩展状态

    则预处理复杂度以及空间复杂度为(O(mlog m)),状态扩展复杂度为(O(3klog k))

    Luogu P2483

    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #include<cctype>
    using namespace std;
    typedef double db;
    typedef pair <db,int> Pair;
    #define mp make_pair
    #define fi first
    #define se second
    #define pb push_back
    #define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
    #define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
    template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
    template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); }
    
    char IO;
    template <class T=int> T rd(){
    	T s=0; int f=0;
    	while(!isdigit(IO=getchar())) f|=IO=='-';
    	do s=(s<<1)+(s<<3)+(IO^'0');
    	while(isdigit(IO=getchar()));
    	return f?-s:s;
    }
    
    const int N=5010,M=2e5+10;
    const db eps=1e-7,INF=1e18;
    
    template <class T> class Heap{
    public:
    	T val;
    	int h;
    	Heap *ls,*rs;
    	T top(){ return val; }
    	Heap(){}
    	Heap(Heap *x):val(x->val),h(x->h),ls(x->ls),rs(x->rs){ ; }
    	Heap(T val):val(val),h(0),ls(0),rs(0){ ; }
    	friend Heap* Union(Heap *x,Heap *y){
    		if(!x) return y;
    		if(!y) return x;
    		if(y->val<x->val) swap(x,y);
    		Heap* u=new Heap(x);
    		u->rs=Union(u->rs,y);
    		if(u->rs && (!u->ls || u->rs->h>u->ls->h)) swap(u->ls,u->rs);
    		u->h=u->rs?u->rs->h+1:1;
    		return u;
    	}
    	Heap* pop(){ return Union(ls,rs); }
    	friend Heap* push(Heap *u,T val){ return Union(u,new Heap(val)); }
    };
    typedef Heap <Pair> Node;
    Node *rt[N];
    
    struct Edge{
    	int to;db w;
    	Edge *nxt;
    	Edge(){}
    	Edge(int to,db w,Edge* nxt):to(to),w(w),nxt(nxt){ ; }
    };
    Edge *head[N],*pre[N];
    void AddEdge(int u,int v,db w){
    	Edge* t=new Edge(v,w,head[u]);
    	head[u]=t;
    }
    int n,m,fa[N];
    db E,dis[N];
    
    void Dijkstra(int u){
    	static priority_queue <Pair,vector<Pair>,greater<Pair>> que;
    	rep(i,1,n) dis[i]=INF;
    	dis[u]=0,que.push({0,u});
    	while(!que.empty()){
    		int u=que.top().se;db d=que.top().fi; que.pop();
    		if(dis[u]<d-eps) continue;
    		for(Edge *i=head[u];i;i=i->nxt) {
    			int v=i->to;
    			if(dis[v]>dis[u]+i->w+eps) pre[v]=i,fa[v]=u,que.push({dis[v]=dis[u]+i->w,v});
    		}
    	}
    }
    
    void Construct(){
    	static int I[N];
    	rep(i,1,n) I[i]=i;
    	sort(I+1,I+n+1,[&](int x,int y){ return dis[x]<dis[y]; });
    	rep(u,1,n) {
    		for(Edge *i=head[u];i;i=i->nxt) {
    			int v=i->to;
    			if(pre[v]==i) continue;
    			rt[v]=push(rt[v],mp(i->w-(dis[v]-dis[u]),u));
    		}
    	}
    	rt[n]=0;
    	rep(j,1,n) {
    		int u=I[j];
    		rt[u]=Union(rt[u],rt[fa[u]]);
    	}
    }
    
    struct State{
    	db s;
    	Node *rt;
    	bool operator < (const State &__) const {
    		return s>__.s;
    	}
    };
    
    int ans;
    void Kth_Path(){
    	static priority_queue <State> que;
    	if(dis[1]>E+eps) return void(puts("0"));
    	ans=1,E-=dis[1];
    	if(rt[1]) que.push({dis[1]+rt[1]->val.fi,rt[1]});
    	while(!que.empty()) {
    		State u=que.top(); que.pop();
    		if(u.s>E+eps) break;
    		int v=u.rt->val.se;
    		ans++,E-=u.s;
    		if(rt[v]) que.push({u.s+rt[v]->top().fi,rt[v]});
    		u.s-=u.rt->val.fi;
    		if(u.rt->ls) {
    			Pair w=u.rt->ls->val;
    			que.push({u.s+w.fi,u.rt->ls});
    		}
    		if(u.rt->rs) {
    			Pair w=u.rt->rs->val;
    			que.push({u.s+w.fi,u.rt->rs});
    		}
    	}
    	printf("%d
    ",ans);
    }
    
    int main(){
    	n=rd(),m=rd(),scanf("%lf",&E);
    	rep(i,1,m){
    		int u=rd(),v=rd();db w; scanf("%lf",&w);
    		AddEdge(v,u,w);
    	}
    	Dijkstra(n);
    	Construct();
    	Kth_Path();
    }
    
  • 相关阅读:
    Linux02--文件系统与磁盘管理
    Android02-Activity01
    Android01-概述
    LeetCode | Binary Tree Level Order Traversal II
    LeetCode | Binary Tree Level Order Traversal
    LeetCode | Binary Tree Postorder Traversal
    LeetCode | Binary Tree Inorder Traversal
    LeetCode | Binary Tree Preorder Traversal
    LeetCode | Symmetric Tree
    LeetCode | Balanced Binary Tree
  • 原文地址:https://www.cnblogs.com/chasedeath/p/14498606.html
Copyright © 2011-2022 走看看