1. 程式人生 > 其它 >「NOIP2009」最優貿易 題解 (最短路SPFA)

「NOIP2009」最優貿易 題解 (最短路SPFA)

題目簡介

咕咕咕。

分析

這道題有多種方法可以實現,我選用的是相對簡單的兩遍\(SPFA\)

看到網上的題解大多都是兩個鄰接表陣列,兩個存邊函式,兩個計算函式,我有些沾沾自喜。

將圖封裝在結構體中,就是香呀~

使用圖 \(a\) 存從 \(1\)\(i\) 的最小進口價格,圖 \(b\) 存從 \(i\)\(n\) 的最大出口價格,再遍歷一遍 i ,最終答案就是

\[max\{b.D(i)-a.D(i)\} \]

帶你設計結構體 \(Edge\)

struct Edge{
        ....
}a,b;

結構體 \(Edge\)

1.鏈式前向星(鄰接表)存邊

struct edge_node{
	int nxt,to;
}e[Maxn];
int head[maxn];
int tot;
inline void add(int u,int v){
	e[++tot].nxt=head[u];
	e[tot].to=v;
	head[u]=tot;
}
int x,y,z;
for(int i=1;i<=m;i++){
	x=read();y=read();z=read();
	a.add(x,y),
	b.add(y,x);
	if(!(z&1))
		a.add(y,x),
		b.add(x,y);
}

2.\(SPFA\)

這裡開始值得注意了。

main() 函式中,我們的呼叫方法是:

a.spfa(1,1);
b.spfa(n,0);

所以此函式的定義方法是:

void spfa(int s,bool cmp)

\(cmp\) 表示的是求解 \(D\) 的方法,\(false\)表示求最小值,\(true\)

表示求最大值。
自然地,\(s\) 表示的是起點位置。

\(SPFA\) 的轉移條件本來是:

\[D[y]=max/min\{D[x]+dis[y]\} \]

這裡應題目需要改成:

\[D[y]=max/min\{max/min\{D[x],price[y]\}\} \]
inline void spfa(int s,bool cmp){
	if(cmp)memset(d,0x3f,sizeof(d));
	queue<int>q;
	d[s]=price[s];
	v[s]=true;
	q.push(s);
	while(!q.empty()){
		int x=q.front();
		q.pop();
		v[x]=false;
		for(int i=head[x];i;i=e[i].nxt){
			int y=e[i].to;
			int z;
			if(cmp)z=mymin(d[x],price[y]);
			else z=mymax(d[x],price[y]);
			if((d[y]>z&&cmp)||(d[y]<z&&(!cmp))){
				d[y]=z;
				if(!v[y])
					q.push(y),
					v[y]=true;
			}
		}
	}
}

\(AC\) 程式碼

如果你久久過不了本題,請看完整程式碼吧:

\(SPFA\):

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int Maxn=maxn<<1;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
inline int mymax(int a,int b){return a>b?a:b;}
inline int mymin(int a,int b){return a<b?a:b;}
int price[maxn];
struct Edge{
	struct edge_node{
		int nxt,to;
	}e[Maxn];
	int head[maxn];
	int tot;
	inline void add(int u,int v){
		e[++tot].nxt=head[u];
		e[tot].to=v;
		head[u]=tot;
	}
	int d[maxn];
	bool v[maxn];
	inline void spfa(int s,bool cmp){
		if(cmp)memset(d,0x3f,sizeof(d));
		queue<int>q;
		d[s]=price[s];
		v[s]=true;
		q.push(s);
		while(!q.empty()){
			int x=q.front();
			q.pop();
			v[x]=false;
			for(int i=head[x];i;i=e[i].nxt){
				int y=e[i].to;
				int z;
				if(cmp)z=mymin(d[x],price[y]);
				else z=mymax(d[x],price[y]);
				if((d[y]>z&&cmp)||(d[y]<z&&(!cmp))){
					d[y]=z;
					if(!v[y])
						q.push(y),
						v[y]=true;
				}
			}
		}
	}
}a,b;
int n,m;
int main(){
	n=read();m=read();
	for(int i=1;i<=n;i++)price[i]=read();
	int x,y,z;
	for(int i=1;i<=m;i++){
		x=read();y=read();z=read();
		a.add(x,y),
		b.add(y,x);
		if(!(z&1))
			a.add(y,x),
			b.add(x,y);
	}
	a.spfa(1,1);
	b.spfa(n,0);
	int ans=0;
	for(int i=1;i<=n;i++){
		ans=mymax(ans,b.d[i]-a.d[i]);
	}
	printf("%d\n",ans);
	return 0;
}

由於沒有負權環,可以考慮使用更穩定的堆優化,雖然對於本題沒有裸 \(SPFA\)

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int Maxn=maxn<<1;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
inline int mymax(int a,int b){return a>b?a:b;}
inline int mymin(int a,int b){return a<b?a:b;}
int price[maxn];
struct Edge{
	struct edge_node{
		int nxt,to;
	}e[Maxn];
	int head[maxn];
	int tot;
	inline void add(int u,int v){
		e[++tot].nxt=head[u];
		e[tot].to=v;
		head[u]=tot;
	}
	int d[maxn];
	bool v[maxn];
	inline void spfa(int s,bool cmp){
		if(cmp)memset(d,0x3f,sizeof(d));
		priority_queue<pair<int,int> >q;
		d[s]=price[s];
		v[s]=true;
		q.push(make_pair(-d[s],s));
		while(!q.empty()){
			int x=q.top().second;
			q.pop();
			v[x]=false;
			for(int i=head[x];i;i=e[i].nxt){
				int y=e[i].to;
				int z;
				if(cmp)z=mymin(d[x],price[y]);
				else z=mymax(d[x],price[y]);
				if((d[y]>z&&cmp)||(d[y]<z&&(!cmp))){
					d[y]=z;
					if(!v[y])
						q.push(make_pair(-d[y],y)),
						v[y]=true;
				}
			}
		}
	}
}a,b;
int n,m;
int main(){
	n=read();m=read();
	for(int i=1;i<=n;i++)price[i]=read();
	int x,y,z;
	for(int i=1;i<=m;i++){
		x=read();y=read();z=read();
		a.add(x,y),
		b.add(y,x);
		if(!(z&1))
			a.add(y,x),
			b.add(x,y);
	}
	a.spfa(1,1);
	b.spfa(n,0);
	int ans=0;
	for(int i=1;i<=n;i++){
		ans=mymax(ans,b.d[i]-a.d[i]);
	}
	printf("%d\n",ans);
	return 0;
}

本題較為良心,不卡 \(SPFA\) ,出於謹慎,可以加一個 \(SLF\) 優化:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int Maxn=maxn<<1;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
inline int mymax(int a,int b){return a>b?a:b;}
inline int mymin(int a,int b){return a<b?a:b;}
int price[maxn];
struct Edge{
	struct edge_node{
		int nxt,to;
	}e[Maxn];
	int head[maxn];
	int tot;
	inline void add(int u,int v){
		e[++tot].nxt=head[u];
		e[tot].to=v;
		head[u]=tot;
	}
	int d[maxn];
	bool v[maxn];
	inline void spfa_slf(int s,bool cmp){
		if(cmp)memset(d,0x3f,sizeof(d));
		deque<int>q;
		d[s]=price[s];
		v[s]=true;
		q.push_back(s);
		while(!q.empty()){
			int x=q.front();
			q.pop_front();
			v[x]=false;
			for(int i=head[x];i;i=e[i].nxt){
				int y=e[i].to;
				int z;
				if(cmp)z=mymin(d[x],price[y]);
				else z=mymax(d[x],price[y]);
				if((d[y]>z&&cmp)||(d[y]<z&&(!cmp))){
					d[y]=z;
					if(!v[y]){
						v[y]=true;
						if(q.empty()||d[y]>d[q.front()])
							q.push_back(y);
						else q.push_front(y);	
					}
				}
			}
		}
	}
}a,b;
int n,m;
int main(){
	n=read();m=read();
	for(int i=1;i<=n;i++)price[i]=read();
	int x,y,z;
	for(int i=1;i<=m;i++){
		x=read();y=read();z=read();
		a.add(x,y),
		b.add(y,x);
		if(!(z&1))
			a.add(y,x),
			b.add(x,y);
	}
	a.spfa_slf(1,1);
	b.spfa_slf(n,0);
	int ans=0;
	for(int i=1;i<=n;i++){
		ans=mymax(ans,b.d[i]-a.d[i]);
	}
	printf("%d\n",ans);
	return 0;
}

$$-----END-----$$