题意
一张
个节点,
条边的有向图,你的车在
号节点,要开到
节点,容量为
(初始时满油)。有若干个点可以无限免费加油,又有若干个地方可以以当地的油价卖出任意容积的油。求从
到
通过一次卖油赚取的最大价值。
思路
由于卖油的地方只有一个,而且只能卖一次,所以这个点肯定是枚举的。构建一张正向图,求出从
出发,开到
,能保有的最大油量,再构建一张反向图,求出从
出发,开到
,能花费的最少油量(就是正向图上从
到
的最小花费)。那这两个数作差就是在
这个点多余,可以卖掉的油。
这道题的最短路和最长路没有
的单调性,能保有的油量在开到加油站时返增不减。这种最短路和最长路问题,用
往往可以方便的解决,但是
极限复杂度是
的,在
的值很大时,往往有其他的解法,
应能避就避。
代码
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define N 1003
#define M 100003
typedef long long LL;
using namespace std;
int dis1[N],dis2[N],sell[N];
template<const int maxn,const int maxm>struct Linked_list
{
int head[maxn],to[maxm],cost[maxm],nxt[maxm],tot;
void clear(){memset(head,-1,sizeof(head));tot=0;}
void add(int u,int v,int w){to[++tot]=v,cost[tot]=w,nxt[tot]=head[u];head[u]=tot;}
#define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
};
Linked_list<N,M>G;
Linked_list<N,M>R;
bool addable[N],vis[N];
int n,m,C;
void solve1(int s)
{
queue<int>q;
while(!q.empty())q.pop();
FOR(i,1,n)dis1[i]=-1;
memset(vis,0,sizeof(vis));
dis1[s]=C;vis[s]=1;
q.push(s);
while(!q.empty())
{
int u=q.front();q.pop();vis[u]=0;
if(addable[u])dis1[u]=C;
EOR(i,G,u)
{
int v=G.to[i],w=G.cost[i];
if(dis1[u]-w>dis1[v]&&dis1[u]-w>=0)
{
dis1[v]=dis1[u]-w;
if(!vis[v])
{
vis[v]=1;
q.push(v);
}
}
}
}
}
void solve2(int s)
{
queue<int>q;
while(!q.empty())q.pop();
FOR(i,1,n)dis2[i]=C+1;
memset(vis,0,sizeof(vis));
dis2[s]=0;vis[s]=1;
q.push(s);
while(!q.empty())
{
int u=q.front();q.pop();vis[u]=0;
if(addable[u])dis2[u]=0;
EOR(i,R,u)
{
int v=R.to[i],w=R.cost[i];
if(dis2[u]+w<dis2[v])
{
dis2[v]=dis2[u]+w;
if(!vis[v])
{
vis[v]=1;
q.push(v);
}
}
}
}
}
int main()
{
while(~scanf("%d%d%d",&n,&m,&C))
{
LL ans=-1;
G.clear();R.clear();
memset(sell,0,sizeof(sell));
FOR(i,1,m)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
G.add(u,v,w);
R.add(v,u,w);
}
memset(addable,0,sizeof(addable));
int t,u,x;
scanf("%d",&t);
while(t--)
{
scanf("%d",&u);
addable[u]=1;
}
solve1(1);
solve2(n);
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&u,&x);
sell[u]=x;
}
FOR(i,1,n)
if(dis1[i]>=dis2[i])
ans=max(ans,1LL*(dis1[i]-dis2[i])*sell[i]);
printf("%lld\n",ans);
}
return 0;
}