题目链接:小doge的快乐阳光跑

我们可以想一下,如果我们最开始肯定是从两个人的起点的其中一个作为起点,而且我们每次肯定是走最短路到达下一个任务点。

所以我们要预处理出所有任务点的最短路,然后每次跑的时候,我们可以想到,走最短路去做的任务有两个,到底是去哪一个任务点呢?

于是我们可以想到dp,dp[i][j][0/1] 代表当前在a的第i个任务点,和b的第j个任务点,并且最后停在了a的任务点,或者b的任务点所需的最短路。

然后dp一下即可。


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1010;
int n,m,a[N],b[N],d[N][N],na,nb,dp[N][N][2];
int head[N],nex[N*20],to[N*20],w[N*20],tot;
inline void add(int a,int b,int c){
	to[++tot]=b; w[tot]=c; nex[tot]=head[a]; head[a]=tot;
}
void dijkstra(int s){
	priority_queue<pair<int,int> > q;	q.push({0,s});	int vis[N]={0};
	d[s][s]=0;
	while(q.size()){
		int u=q.top().second;	q.pop();
		if(vis[u])	continue;	vis[u]=1;
		for(int i=head[u];i;i=nex[i]){
			if(d[s][to[i]]>d[s][u]+w[i]){
				d[s][to[i]]=d[s][u]+w[i];
				if(!vis[to[i]])	q.push({-d[s][to[i]],to[i]});
			}
		}
	}
}
signed main(){
	scanf("%lld %lld",&n,&m);
	while(m--){
		int a,b,c;	scanf("%lld %lld %lld",&a,&b,&c);	add(a,b,c);	add(b,a,c);
	}
	scanf("%lld",&na);
	for(int i=1;i<=na;i++)	scanf("%lld",&a[i]);
	scanf("%lld",&nb);
	for(int i=1;i<=nb;i++)	scanf("%lld",&b[i]);
	memset(d,0x3f,sizeof d);
	for(int i=1;i<=na;i++){
		if(!d[a[i]][a[i]])	continue;	dijkstra(a[i]);
	}
	for(int i=1;i<=nb;i++){
		if(!d[b[i]][b[i]])	continue;	dijkstra(b[i]);
	}
	memset(dp,0x3f,sizeof dp);
	for(int i=0;i<=n;i++)	d[0][i]=0;	dp[0][0][0]=dp[0][0][1]=0;
	for(int i=0;i<=na;i++){
		for(int j=0;j<=nb;j++){
			if(i){
				dp[i][j][0]=min(dp[i][j][0],dp[i-1][j][0]+d[a[i-1]][a[i]]);
				dp[i][j][0]=min(dp[i][j][0],dp[i-1][j][1]+d[b[j]][a[i]]);
			}
			if(j){
				dp[i][j][1]=min(dp[i][j][1],dp[i][j-1][0]+d[a[i]][b[j]]);
				dp[i][j][1]=min(dp[i][j][1],dp[i][j-1][1]+d[b[j-1]][b[j]]);
			}			
		}
	}
	printf("%lld\n",min(dp[na][nb][0],dp[na][nb][1]));
	return 0;
}