题意:在一张节点带有权值(点权)的图上找出一条从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; }