1. 程式人生 > 實用技巧 >最優貿易(SPFA)

最優貿易(SPFA)

題意:在一張節點帶有權值(點權)的圖上找出一條從1到n的路徑,使路徑上能選出兩個點p,q(先經過p在經過q),並且“節點q的權值減去p的權值”最大。

思路:

1. 列舉每一個節點 i ,1 ~ i 按照最短路來求,記錄1 ~ i 中最短的邊是 dim [ i ].

2. 反向建圖,列舉每一個節點 i ,N ~ i 按照最大路來求,記錄N ~ i 中最長的邊是 dim [ i ].

(反向建圖,這裡要想明白,假設1->2->3, 我們從節點1開始跑,我們反向之後變成3->2->1,我們從節點3開始跑。

其實是一樣的)。

說一下這題為什麼不能用Dij , 假設存在邊 5-> 6, 6-> 7, 7-> 5 。很明顯這是存在環的,如果用Dij 我們假設dim[ 5 ]=10,然後6處的價格是4,更新,

但是 7處價格是 3,更新, dim[ 7 ]=3.,我麼發現 7又和5相連,此時dim[5] 被更新成 3,假設 3是當前最小佇列中的最小值,我們就要把 5再次取出,但是5已經被標記過了,不能再被取出,這就導致了後面的dim[ 6 ] 還是4,更新不成3.導致答案錯誤。(主要原因是圖中存在環路,當前取出的值不一定是最小值,所以就不能用Dij)。

#include<bits/stdc++.h>
#include<cstring>
#include<string.h>
#include<cstdio>
using namespace std;
const int N=1e5+10; int dmin[N],dmax[N],n,m; int w[100005]; bool vis1[N],vis2[N]; struct node{ int to; }; vector<node> vec1[N],vec2[N]; void spfa1() { /*for(int i = 1; i <= n; ++i) cout << dmin[i] << " "; cout <<endl;*/ queue<int>q; q.push(1); dmin[
1]=w[1]; vis1[1]=1; while(!q.empty()){ int u=q.front(); //cout << "u = " << u <<endl; q.pop(); vis1[u]=0; int _size=vec1[u].size(); for(int i=0; i<_size; i++){ int to=vec1[u][i].to; if(dmin[to]>min(w[to],dmin[u])){ dmin[to]=min(w[to],dmin[u]); /* 更新最小值。用到 u 的最小的邊,和 u ~ v 相連的邊進行比較 */ if(!vis1[to]){ q.push(to); vis1[to]=1; } } } } //cout<<"**"<<endl; } void spfa2(){ queue<int>q; q.push(n); dmin[n]=w[n]; vis1[n]=1; while(!q.empty()){ int u=q.front(); q.pop(); vis2[u]=0; int _size=vec2[u].size(); for(int i=0; i<_size; i++){ int to=vec2[u][i].to; if(dmax[to]<max(w[to],dmax[u])){ dmax[to]=max(w[to],dmax[u]); /* 更新最大值。用到 u 的最大的邊,和 u ~ v 相連的邊進行比較 */ if(!vis2[to]){ q.push(to); vis2[to]=1; } } } } } int main() { scanf("%d%d",&n,&m); for(int i=1; i<=n; i++){ cin>>w[i]; } int x,y,z; for(int i=1; i<=m; i++) { scanf("%d%d%d",&x,&y,&z); if(z==1) vec1[x].push_back({y}),vec2[y].push_back({x}); else vec1[x].push_back({y}),vec1[y].push_back({x}),vec2[y].push_back({x}),vec2[x].push_back({y}); } memset(dmin,0x3f,sizeof (dmin)); /*memset(vis1,0,sizeof (vis1)); memset(vis2,0,sizeof (vis2));*/ spfa1(); spfa2(); int ans=0; for(int i=1; i<=n; i++) { // cout<<dmin[i]<<" "<<dmax[i]<<endl; ans=max(ans,dmax[i]-dmin[i]); } printf("%d",ans); return 0; }
View Code