题目:http://www.luogu.org/problem/show?pid=1073#
分析:两次spfa,
第一次spfa从起点开始,算出到达每个点i时的最小买入价in[i]
第二次spfa把所有边反向,终点开始spfa,算出到达每个点i时的最大卖出价out[i]
ans=max(out[i]-in[i]),1<=i<=n;
然而一开始想到一个O(log(100)*n+n)的算法,但是忽然发现会被卡成O(n^3)的。一开始是这么想的:两遍bfs,第一遍判起点能否走到每个点,第二遍反向边判每个点能否走到终点。然后二分答案差价,bfs判断差价的两端点是否可以相互到达并且可以走到起点和终点。然而注意到n很大,价格很小,所以很多价格重复的节点,log100实际近似于n^2…….
代码:
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int Tmax=100005,Tmax2=500005;
int n,m,u[Tmax2*2],val[Tmax],v[Tmax2*2],last;
int in[Tmax],out[Tmax],ans;
bool vis[Tmax];
vector<int> G[Tmax];
queue<int> Q;
void spfa()
{
int i,x,len,to;
Q.push(1);
memset(in,0x3f,sizeof(in));
in[1]=val[1];
while(!Q.empty())
{
x=Q.front();
Q.pop();
vis[x]=false;
in[x]=min(in[x],val[x]);
len=G[x].size();
for(i=0;i<len;i++)
{
to=v[G[x][i]];
if(in[x]<in[to])
{
in[to]=in[x];
if(vis[to]==false)
{
Q.push(to);
vis[to]=true;
}
}
}
}
while(!Q.empty()) Q.pop();
for(i=1;i<=n;i++)
G[i].clear();
for(i=1;i<=last;i++)
{
swap(u[i],v[i]);
G[u[i]].push_back(i);
}
Q.push(n);
memset(vis,0,sizeof(vis));
out[n]=val[n];
while(!Q.empty())
{
x=Q.front();
Q.pop();
vis[x]=false;
out[x]=max(out[x],val[x]);
len=G[x].size();
for(i=0;i<len;i++)
{
to=v[G[x][i]];
if(out[x]>out[to])
{
out[to]=out[x];
if(vis[to]==false)
{
Q.push(to);
vis[to]=true;
}
}
}
}
return;
}
int main()
{
int i,z;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
scanf("%d",&val[i]);
for(i=1;i<=m;i++)
{
last++;
scanf("%d%d%d",&u[last],&v[last],&z);
G[u[last]].push_back(last);
if(z==2){
last++;
u[last]=v[last-1];
v[last]=u[last-1];
G[u[last]].push_back(last);
}
}
spfa();
for(i=1;i<=n;i++)
ans=max(ans,out[i]-in[i]);
printf("%d",ans);
return 0;
}