1. 程式人生 > 實用技巧 >乘車路線「二維spfa」

乘車路線「二維spfa」

乘車路線「二維spfa」

題目描述

編號為 1NN 座城鎮用若干僅供單向行駛的道路相連,每條道路上均有兩個引數:道路長度(length)和在該條道路上行駛的費用(cost)。BOB準備從城鎮 1 出發到達城鎮 N,但他目前只有 W 的錢,為此,你需要幫助他尋找一條從城鎮 1 到城鎮 N在他能支付的前提下的一條最短路線。

輸入

W N MN為城鎮數目,2<=N<=100M 為道路條數,1<=M<=10000,W 為錢的數目,0<=W<=1000)隨後的 M行每行為一條道路的資訊,包含 4 個數值(u,v,w,cost),表示從城鎮 uv 有一條長度為 w 的單向道路,經過這條道路需要花費 cost

。(1<=u,v<=N,1<=w<=100,0<=cost<=100)

輸出

輸出最短長度,若無解,則輸出“NO ”;

樣例輸入

5 6 7
1 2 2 3
2 4 3 3
3 4 2 4
1 3 4 1
4 6 2 1
3 5 2 0
5 4 3 2

樣例輸出

11

思路分析

  • 乍一眼看去,最短路啊,再看一眼,誒?有問題,還多了一個限制條件就離譜
  • 考試時直接按邊權和錢數分別跑了兩遍spfa,後來才發現正確性是不對的(更離譜的是竟然水過了80分,最離譜的是有人暴搜AC了???)。
  • 那麼正確思路是什麼?兩個一起跑唄,一個二維spfa就彳亍,用到了一個本蒟蒻沒用過的pair

詳見程式碼

程式碼

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>

using namespace std;
const int maxn = 100+10,maxm = 1e4+10,inf = 0x3f3f3f3f;
int w,n,m;
int dis[maxn][1005],mon[maxn],ans[maxn],head[maxm],vis[maxn][1005];
struct edge{
	int to,next,w,cost;
}e[maxm];
int len;
void addedge(int u,int v,int x,int y){
	e[++len].to = v;
	e[len].w = x;
	e[len].cost = y;
	e[len].next = head[u];
	head[u] = len;
}
void spfa(int x){ //注意dis,vis都開二維
	memset(dis,0x3f,sizeof(dis));
	queue< pair<int,int> >q; //佇列裡儲存的是pair
	dis[x][0] = 0;
	q.push(make_pair(x,0)); //用一個pair把dis和money一起壓進去
	while(!q.empty()){
		pair<int,int> t = q.front();q.pop();
		int u = t.first,money = t.second;
		vis[u][money] = 0;
		for(int i = head[u];~i;i = e[i].next){
			int v = e[i].to;
			if(money+e[i].cost>w)continue; //錢不夠跑
			if(dis[v][money+e[i].cost]>dis[u][money]+e[i].w){ //錢夠跑並且可以鬆弛
				dis[v][money+e[i].cost]=dis[u][money]+e[i].w;
				if(!vis[v][money+e[i].cost]){
					vis[v][money+e[i].cost] = 1;
					q.push(make_pair(v,money+e[i].cost));
				}
			}
		}
	}
}

int main(){
	scanf("%d%d%d",&w,&n,&m);
	memset(head,-1,sizeof(head));
	for(int i = 1;i <= m;i++){
		int a,b,c,d;scanf("%d%d%d%d",&a,&b,&c,&d);
		addedge(a,b,c,d);
	}
	spfa(1);
	int ans = inf;
	for(int i = 0;i <= w;i++)ans = min(ans,dis[n][i]);
	ans==inf ? printf("NO") : printf("%d",ans);
	return 0;
}