1. 程式人生 > >【BZOJ2407/4398】探險/福慧雙修 最短路建模

【BZOJ2407/4398】探險/福慧雙修 最短路建模

同時 amp lin bsp next 可能 ring string min

【BZOJ2407】探險

Description

探險家小T好高興!X國要舉辦一次溶洞探險比賽,獲獎者將得到豐厚獎品哦!小T雖然對獎品不感興趣,但是這個大振名聲的機會當然不能錯過! 比賽即將開始,工作人員說明了這次比賽的規則:每個溶洞和其他某些溶洞有暗道相連。兩個溶洞之間可能有多條道路,也有可能沒有,但沒有一條暗道直接從自己連到自己。參賽者需要統一從一個大溶洞出發,並再次回到這個大溶洞。 如果就這麽點限制,那麽問題就太簡單了,可是舉辦方又提出了一個條件:不能經過同一條暗道兩次。這個條件讓大家犯難了。這該怎麽辦呢? 到了大溶洞口後,小T愉悅地發現這個地方他曾經來過,他還記得有哪些暗道,以及通過每條暗道的時間。小T現在向你求助,你能幫他算出至少要多少時間才能回到大溶洞嗎?

Input

第一行兩個數n,m表示溶洞的數量以及暗道的數量。

接下來m行,每行4個數s、t、w、v,表示一個暗道連接的兩個溶洞s、t,這條暗道正著走(s à t)的所需要的時間w,倒著走(t à 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;
}

【BZOJ2407/4398】探險/福慧雙修 最短路建模