该题和普通最短路的区别,多了一个中间值。即不是直接u->v,而是u,t->v
bfs从前往后(s->t)
优先队列+矩阵
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e3+5;
const int inf=0x3f3f3f3f;
int n,m,k,t,a[maxn],dis[maxn],vis[maxn],g[maxn][maxn],x,y,c;
struct nn{
int v,t;
friend bool operator < (nn a,nn b){
return a.t>b.t;
}
};
priority_queue<nn> q;
void bfs(){
nn cur,tem;
while(!vis[t]){
cur=q.top();
q.pop();
int u=cur.v;
if(!vis[u]){//push在q里的,是用已有的可以合成的,但还没合成
vis[u]=1;
//这一步才合成了u,改变vis。才能使其它的可以合成,用for来找
for(int i=1;i<=n;i++){
if(g[u][i]!=inf){//有路
int v=g[u][i];
if(!vis[v]&&vis[i]){//没有合成过v才进去,剪枝
int tt=dis[u]+max(a[u],a[i]);
if(tt<dis[v]){
dis[v]=tt;
q.push({v,tt});
//已有的材料可以合成的push
}
}
}
}
}
}
return ;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&k,&t);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
dis[i]=inf;vis[i]=0;
for(int j=1;j<=n;j++){
g[i][j]=inf;
}
}
for(int i=0;i<m;i++){
scanf("%d",&x);
// q.push({x,0});不用放入,没意义
vis[x]=1;dis[x]=0;
}
for(int i=0;i<k;i++){
scanf("%d%d%d",&x,&y,&c);
g[x][y]=g[y][x]=c;
int tt=max(a[x],a[y]);
if(vis[x]&&vis[y]&&tt+dis[x]<dis[c]){
dis[c]=tt+dis[x];
q.push({c,dis[c]});
}
}
bfs();
cout<<dis[t]<<endl;
return 0;
}
优先队列+链式前向星
#include <bits/stdc++.h>
using namespace std;
const int maxn=2020;
const int maxm=2e5+10;
const int inf=0x3f3f3f3f;
int n,m,k,key,x,y,z,cnt=0;
int a[maxn],dis[maxn],vis[maxn],head[maxn];
int e[maxm],to[maxm],cost[maxm],nex[maxm];
void creat(int u,int v,int t,int c){
e[++cnt]=v;//经过的点
to[cnt]=t;//到达的点
cost[cnt]=c;
nex[cnt]=head[u];
head[u]=cnt;
}
struct nn{
int v,tt;
friend bool operator < (nn a,nn b){
return a.tt>b.tt;//小顶堆,用>
}
};
priority_queue<nn> q;
void bfs(){
nn cur,tem;
while(!q.empty()){
cur=q.top();
q.pop();
int u=cur.v;
if(!vis[u]){
vis[u]=1;
//已有的才能合成
for(int i=head[u];i;i=nex[i]){
int v=e[i],t=to[i],c=cost[i];
if(!vis[t]&&vis[v]){
int tt=dis[u]+c;
if(tt<dis[t]){
dis[t]=tt;
q.push({t,tt});
}
}
}
}
}
return ;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&k,&key);
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
dis[i]=inf;
}
for(int i=0;i<m;i++){
scanf("%d",&x);
// q.push({x,0}); 不用放入,没意义
vis[x]=1;
dis[x]=0;
}
for(int i=0;i<k;i++){
scanf("%d%d%d",&x,&y,&z);
int tt=max(a[x],a[y]);
if(vis[x]&&vis[y]&&tt+dis[x]<dis[z]){
dis[z]=tt+dis[x];
q.push({z,dis[z]});
}
creat(x,y,z,tt);
creat(y,x,z,tt);
}
bfs();//最短路,bfs是void
cout<<dis[key]<<endl;//输出直接看dis[target]
return 0;
}
dfs 从后往前
#include<bits/stdc++.h>
using namespace std;
const int maxn=2010;
int n,m,k,mb,a[maxn],vis[maxn];//vis记录该点是否已合成
struct node{
int num;//记录有几种合成方法
int u[maxn],v[maxn];
int mint;//合成该点 用的最短时间
}fa[maxn];
//dfs就是从结尾往前找。
int dfs(int x){//可以返回x能否被合成
if(!fa[x].num) return 0;//=0表示没有可以合成x的
for(int i=1;i<=fa[x].num;i++){
int u=fa[x].u[i],v=fa[x].v[i];
//如果uv中有一个没出现过,dfs看看能否合成。当俩都出现过才能合成x
if(!vis[u])
if(!dfs(u))continue;
if(!vis[v])
if(!dfs(v))continue;
//这俩要都生成了,才能合成x ,取max满足俩都生成
int tt=max(fa[u].mint,fa[v].mint)+max(a[u],a[v]);
if(!fa[x].mint) fa[x].mint=tt;//mint==0表示x还没合成过。初始化都为0
else fa[x].mint=min(fa[x].mint,tt);
}
vis[x]=1;//一旦dfs到底发现不能合成return0.所以没返回0的就是能合成的
return 1;
}
int main(){
int x,y,z;
scanf("%d%d%d%d",&n,&m,&k,&mb);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
vis[i]=0;
}
for(int i=1;i<=m;i++){
scanf("%d",&x);
vis[x]=1;
}
for(int i=1;i<=k;i++){
scanf("%d%d%d",&x,&y,&z);
int tem=++fa[z].num;
fa[z].u[tem]=x;
fa[z].v[tem]=y;
}
dfs(mb);
cout<<fa[mb].mint<<endl;
}