题目大意:给定一个无向图,边上有权值,顶点也有权值。有q次询问,每次询问两个顶点,求这两个顶点之间(的路加上这条路上的最大的顶点权值)的最小值。
思路:枚举点,然后求该点到a,b的最小距离。spfa的期望复杂度是O(ke),k一般在2左右,然后,就可以在这道题目大显身手了,这题时限非常紧,一般都是几千毫秒过的,时限只给了5000MS。所以,裸的spfa和暴力询问还是会超时。
关键方程:(start为你枚举的点) if(val[v]<=val[start]&&dis[v]>dis[top]+edge[i].w)
if(dis[u]!=inf&&dis[v]!=inf&&ans[i]>dis[u]+dis[v]+val[start])
ans[i]=dis[u]+dis[v]+val[start];
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
#define MAXN 1005
#define MAXM 40005
#define inf 10000000000007LL
using namespace std;
int head[MAXN];
int cnt=0;
int n,m;
long long val[MAXN];
int qua[MAXM],qub[MAXM];
int q;
long long ans[MAXM];
int vis[MAXN];
long long dis[MAXN];
int mystack[MAXM*5];
struct Node
{
int v;
long long w;
int next;
}edge[MAXM];
void init()
{
cnt=0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v,long long w)
{
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt++;
}
void spfa(int start)
{
for(int i=0;i<=n+3;i++)
vis[i]=0,dis[i]=inf;
vis[start]=1;
dis[start]=0;
int h=0,r=0;
mystack[r++]=start;
while(h<r)
{
int top=mystack[h++];
vis[top]=0;
for(int i=head[top];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(val[v]<=val[start]&&dis[v]>dis[top]+edge[i].w)
{
dis[v]=dis[top]+edge[i].w;
if(!vis[v])
{
mystack[r++]=v;
vis[v]=1;
}
}
}
}
for(int i=1;i<=q;i++)
{
int u=qua[i];
int v=qub[i];
if(dis[u]!=inf&&dis[v]!=inf&&ans[i]>dis[u]+dis[v]+val[start])
ans[i]=dis[u]+dis[v]+val[start];
}
}
int main()
{
int u,v;
__int64 w;
while(scanf("%d%d",&n,&m)==2)
{
if(n==0&&m==0)break;
init();
for(int i=1;i<=n;i++)
scanf("%I64d",&val[i]);
for(int i=1;i<=m;i++)
{
scanf("%d%d%I64d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
scanf("%d",&q);
for(int i=1;i<=q;i++)
scanf("%d%d",&qua[i],&qub[i]),ans[i]=inf;
for(int i=1;i<=n;i++)
spfa(i);
for(int i=1;i<=q;i++)
{
if(ans[i]==inf)
printf("-1\n");
else
printf("%I64d\n",ans[i]);
}
puts("");
}
return 0;
}