题目大意:给定一张有向图,求从起点到终点最大值与最小值的差值(这么说还不准确,下文有解释)的最大值。
思路:可以tarjan+DP,不过SPFA是可以解决的。由于只能先买入再卖出,因此要先正向spfa求出到某一点时可能的最小值,再反向spfa求出到某一点时可能的最大值,同时记录点的访问情况,这样两次都被访问过的点就在起点到终点的路径上。最后扫描这些点即可。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int maxn=100005;
int n,m,a[maxn],ma[maxn],mi[maxn];
vector<int> g[maxn],rg[maxn];
bool can[maxn],rcan[maxn];
void init()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]);
for (int i=1;i<=m;++i)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
if (c==1)
{
g[a].push_back(b);
rg[b].push_back(a);
}
else
{
g[a].push_back(b);
rg[b].push_back(a);
g[b].push_back(a);
rg[a].push_back(b);
}
}
}
queue<int> q;
bool vis[maxn];
void rspfa(int s)
{
memset(vis,0,sizeof(vis));
memset(ma,0,sizeof(ma));
memset(rcan,0,sizeof(rcan));
vis[s]=1;
rcan[s]=1;
ma[s]=a[s];
q.push(s);
while (!q.empty())
{
int h=q.front();
q.pop();
vis[h]=0;
for (int i=0;i<rg[h].size();++i)
{
if (ma[rg[h][i]]<max(ma[h],a[rg[h][i]]))
{
ma[rg[h][i]]=max(ma[h],a[rg[h][i]]);
if (!rcan[rg[h][i]])
rcan[rg[h][i]]=1;
if (!vis[rg[h][i]])
{
vis[rg[h][i]]=1;
q.push(rg[h][i]);
}
}
}
}
}
void spfa(int s)
{
memset(vis,0,sizeof(vis));
memset(mi,0x3f3f3f,sizeof(mi));
memset(can,0,sizeof(can));
vis[s]=1;
mi[s]=a[s];
can[s]=1;
q.push(s);
while (!q.empty())
{
int h=q.front();
q.pop();
vis[h]=0;
for (int i=0;i<g[h].size();++i)
{
if (mi[g[h][i]]>min(mi[h],a[g[h][i]]))
{
mi[g[h][i]]=min(mi[h],a[g[h][i]]);
if (!can[g[h][i]])
can[g[h][i]]=1;
if (!vis[g[h][i]])
{
vis[g[h][i]]=1;
q.push(g[h][i]);
}
}
}
}
}
int main()
{
init();
spfa(1);
rspfa(n);
int ans=0;
for (int i=1;i<=n;++i)
if (can[i] && rcan[i])
if (ma[i]!=0 && mi[i]!=0x3f3f3f && ma[i]>=mi[i])
ans=max(ans,ma[i]-mi[i]);
printf("%d",ans);
return 0;
}