zoukankan      html  css  js  c++  java
  • [bzoj2407]探险——重构图+最短路

    题目大意:

    给定一个无向图,每一条边正着和反着都有一个边权,求一条不经过重复边的路径,使得边权和最小。

    思路:

    这个题目的思路比较巧妙
    网络上的题解只有做法,没有详细地解释。
    考虑最暴力的方法,对于1号点能够直接到达的每一个点,把它和1号点的边删掉以后,以这个点为源点跑最短路。
    但是这样会T飞。
    不妨先放下不能重复走的限制,建立一个新的虚拟汇点t=n+1,然后将所有连向1的边转化成连向t的边。然后对于1到t跑最短路算法
    这样子做显然有一些点问题,有的路径可能刚刚才走出去就又回来了,这样是不合法的。
    例如一条边(1,v,w),在转移中可能会是这个样子的 (1->v->t)
    那么如果我们将(1,v,w)给去掉,将可能从(1,v,w)这条边出发的路径转化为一些边(1,u,dis[1...u]) (1,v,w)(in) this path
    使得这些u存在于所有(1->t)的路径上,并且对于所有的u,都有1到u的最短路的第一个点不是v。
    这样以后我们再去求最短路,便不用担心一条边跑出去再跑回去的问题,因为有了上面的限制,通过这条边出去之后再回去一定不是最短的。
    虑怎么建立上面所说的等效边,可以对于原图做一次最短路,记录每个点的最短路径的第一个点f[u],枚举每一条边(u,v,w),如果f[u]!=f[v],那么则说明这是f[u]和f[v]的范围的分界处,在这里分别对f[u]和f[v]都建立等效边即可。
    当然如果u=1或者v=1的情况需要特殊考虑,但是方法是类似的。具体方法可以参考hzwer的博客。

    #include<bits/stdc++.h>
    
    #define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
    #define pii pair<int,int>
    #define fi first
    #define se second
    #define mk make_pair
    typedef long long ll;
    
    using namespace std;
    
    void File(){
    	freopen("bzoj2407.in","r",stdin);
    	freopen("bzoj2407.out","w",stdout);
    }
    
    template<typename T>void read(T &_){
    	T __=0,mul=1; char ch=getchar();
    	while(!isdigit(ch)){
    		if(ch=='-')mul=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
    	_=__*mul;
    }
    
    const int maxn=1e4+10;
    const int maxm=2e5+10;
    int n,m,dis[maxn],fr[maxn];
    int beg[maxn],from[maxm<<1],to[maxm<<1],las[maxm<<1],w[maxm<<1],cnte=1;
    priority_queue< pii,vector<pii>,greater<pii> >qu;
    
    void add(int u,int v,int val){
    	las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v; from[cnte]=u; w[cnte]=val;
    }
    
    void Dijkstra(){
    	memset(dis,63,sizeof(dis));
    	memset(fr,0,sizeof(fr));
    	dis[1]=0; qu.push(mk(0,1));
    	while(!qu.empty()){
    		int u=qu.top().se,d=qu.top().fi;
    		qu.pop();
    		if(d!=dis[u])continue;
    		for(int i=beg[u];i;i=las[i]){
    			int v=to[i];
    			if(d+w[i]<dis[v]){
    				fr[v]= u==1 ? i/2 : fr[u];
    				dis[v]=d+w[i];
    				qu.push(mk(dis[v],v));
    			}
    		}
    	}
    }
    
    void rebuild(){
    	int sz=cnte; cnte=0;
    	memset(beg,0,sizeof(beg));
    	REP(i,2,sz){
    		if(from[i]==1){
    			if(fr[to[i]]!=i/2)
    				add(from[i],to[i],w[i]);
    		}
    		else if(to[i]==1){
    			if(fr[from[i]]==i/2)
    				add(from[i],n+1,w[i]);
    			else add(1,n+1,dis[from[i]]+w[i]);
    		}
    		else{
    			if(fr[from[i]]==fr[to[i]])
    				add(from[i],to[i],w[i]);
    			else add(1,to[i],dis[from[i]]+w[i]);
    		}
    	}
    }
    
    int main(){
    	File();
    	read(n); read(m);
    	int u,v,val0,val1;
    	REP(i,1,m){
    		read(u),read(v),read(val0),read(val1);
    		add(u,v,val0),add(v,u,val1);
    	}
    	Dijkstra();
    	rebuild();
    	Dijkstra();
    	printf("%d
    ",dis[n+1]);
    	return 0;
    }
    
    
  • 相关阅读:
    hibernate事务管理
    oracle的中文排序问题
    hibernate一级缓存
    Hibernate的实体规则、主键生成策略、对象状态
    【SVN】命令行忽略不必要的文件和文件夹
    【SVN】SVN的trunk、branches、tag的使用以及分支的概念
    hibernate介绍及环境搭建
    敏捷实践:比每日会议更疯狂的半日会议!
    Android开发之有效获取状态栏(StatusBar)高度
    jquery判断输入文字个数的统计代码
  • 原文地址:https://www.cnblogs.com/ylsoi/p/9861095.html
Copyright © 2011-2022 走看看