zoukankan      html  css  js  c++  java
  • 【BZOJ2407/4398】探险/福慧双修 最短路建模

    【BZOJ2407】探险

    Description

    探险家小T好高兴!X国要举办一次溶洞探险比赛,获奖者将得到丰厚奖品哦!小T虽然对奖品不感兴趣,但是这个大振名声的机会当然不能错过!
    比赛即将开始,工作人员说明了这次比赛的规则:每个溶洞和其他某些溶洞有暗道相连。两个溶洞之间可能有多条道路,也有可能没有,但没有一条暗道直接从自己连到自己。参赛者需要统一从一个大溶洞出发,并再次回到这个大溶洞。
    如果就这么点限制,那么问题就太简单了,可是举办方又提出了一个条件:不能经过同一条暗道两次。这个条件让大家犯难了。这该怎么办呢?
    到了大溶洞口后,小T愉悦地发现这个地方他曾经来过,他还记得有哪些暗道,以及通过每条暗道的时间。小T现在向你求助,你能帮他算出至少要多少时间才能回到大溶洞吗?

    Input

    第一行两个数n,m表示溶洞的数量以及暗道的数量。

    接下来m行,每行4个数s、t、w、v,表示一个暗道连接的两个溶洞s、t,这条暗道正着走(à t)的所需要的时间w,倒着走(à s)所需要的时间v。由于溶洞的相对位置不同,wv可能不同。

    Output

    输出一行一个数t,表示最少所需要的时间。

    Sample Input

    3 3
    1 2 2 1
    2 3 4 5
    3 1 3 2

    Sample Output

    8

    HINT

    N<=10000,M<=200000,1<=W,V<=10000

    题解:如果想不经过重复的边回到1号点,那么只需要满足从1出发时走的点不同于回到1时走的点即可。那么我们先跑一边spfa,在求最短路的同时维护一个pre数组,代表如果沿最短路走到i,则从i出发时走的第一个点是pre[i]。那么我们所求的路径一定是从一些pre=x的点走到一些pre!=x的点再走到1号点。那么我们可以采用如下方法构造新图。

    新建汇点n+1,对于边(u,v,len),如果pre[u]!=pre[v],则直接连从1到v,边权为dis[u]+len的边;否则连从u到v,边权len的边。
    特别地,如果u=1,那么若pre[v]!=v,连从1到v边权dis[v]的边;否则不连。
    如果v=1,那么若pre[u]!=u,直接用dis[u]+len更新答案;否则连u到n+1,边权len的边。

     

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <queue>
    #include <utility>
    #define mp(A,B) make_pair(A,B)
    using namespace std;
    const int maxn=10010;
    const int maxm=400010;
    int n,m,cnt,ans;
    queue<int> q;
    int pre[maxn],inq[maxn],dis[maxn],pa[maxm],pb[maxm],pc[maxm];
    int to[maxm],next[maxm],val[maxm],head[maxn];
    void add(int a,int b,int c)
    {
    	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
    }
    void spfa()
    {
    	int u,i;
    	while(!q.empty())
    	{
    		u=q.front(),q.pop(),inq[u]=0;
    		for(i=head[u];i!=-1;i=next[i])	if(dis[to[i]]>dis[u]+val[i])
    		{
    			dis[to[i]]=dis[u]+val[i],pre[to[i]]=pre[u];
    			if(!inq[to[i]])	inq[to[i]]=1,q.push(to[i]);
    		}
    	}
    }
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    int main()
    {
    	n=rd(),m=rd();
    	int i,a,b,c,d;
    	memset(head,-1,sizeof(head));
    	for(i=1;i<=m;i++)
    		a=rd(),b=rd(),c=rd(),d=rd(),add(a,b,c),pa[cnt]=a,pb[cnt]=b,pc[cnt]=c,add(b,a,d),pa[cnt]=b,pb[cnt]=a,pc[cnt]=d;
    	memset(dis,0x3f,sizeof(dis));
    	dis[1]=0;
    	for(i=head[1];i!=-1;i=next[i])	dis[to[i]]=val[i],q.push(to[i]),inq[to[i]]=1,pre[to[i]]=to[i];
    	spfa();
    	memset(head,-1,sizeof(head)),cnt=0;
    	ans=1<<30;
    	for(i=1;i<=2*m;i++)
    	{
    		if(pa[i]==1)
    		{
    			if(pre[pb[i]]!=pb[i])	add(1,pb[i],pc[i]);
    		}
    		else	if(pb[i]==1)
    		{
    			if(pre[pa[i]]==pa[i])	add(pa[i],n+1,pc[i]);
    			else	ans=min(ans,dis[pa[i]]+pc[i]);
    		}
    		else
    		{
    			if(pre[pa[i]]==pre[pb[i]])	add(pa[i],pb[i],pc[i]);
    			else	add(1,pb[i],dis[pa[i]]+pc[i]);
    		}
    	}
    	memset(dis,0x3f,sizeof(dis));
    	dis[1]=0,q.push(1);
    	spfa(),ans=min(ans,dis[n+1]);
    	if(ans==1<<30||ans==0x3f3f3f3f)	printf("-1");
    	else	printf("%d",ans);
    	return 0;
    }

     

  • 相关阅读:
    软件测试:Homework 3
    软件测试:Lab 1
    软件测试:Homework 2
    软件测试:Homework 1
    JAVA的回忆
    Java 操作符
    Java 笔录
    经典C#编程理解,概要,经典
    网络精灵
    签到计时
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7468799.html
Copyright © 2011-2022 走看看