题:https://ac.nowcoder.com/acm/contest/5670/A
题意:给出n个点m条边的有边权连通图,k个任务,每个任务表示为[u,v],表示必须走到u节点再走到v节点,任务必须按1~k次序完成。其中你可以在经过的节点上设置传送点,俩个传送点之间的代价为0,图上最多有2个传送点,问完成k个任务的最小路径和是多少
分析:对于每个任务,由于是要依次完成,所以将k个任务的2*k的点连起来形成一个路径a[i],定义dp[i][j]表示完成了前 i 个任务节点,传送门的位置在 j 节点;
转移:(1)a[i]直接走向a[i+1];
(2)dp[i][p]向dp[i+1][q]转移(在p和q设传送点):因为dp[i][p]的传送点在p,而题目条件是只能在走过的地方设传送点,所以路径是a[i]-q-p-a[i+1](而不是a[i]-p-q-a[i+1]);
(3)直接在a[i]传到p,然后走到q,在q设传送点,再走到a[i+1]。(这里是要达到dp[i+1][q]在q点有传送点)

#include<bits/stdc++.h> using namespace std; #define pb push_back #define MP make_pair typedef long long ll; const int mod=1e9+7; const int M=303; const int inf=0x3f3f3f3f; const ll INF=1e18; ll dis[M][M],dp[M*2][M]; int a[M*2]; int main(){ int n,m,k; scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(i!=j) dis[i][j]=INF; for(int i=1;i<=m;i++){ int u,v; ll w; scanf("%d%d%lld",&u,&v,&w); dis[v][u]=dis[u][v]=min(dis[u][v],w); } for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); for(int i=1;i<=2*k;i++) scanf("%d",&a[i]); a[0]=1; for(int i=0 ;i<=2*k;i++) for(int j=1;j<=n;j++) dp[i][j]=INF; dp[0][1]=0; for(int i=0;i<2*k;i++){ for(int p=1;p<=n;p++){ dp[i+1][p]=min(dp[i+1][p],dp[i][p]+dis[a[i]][a[i+1]]); for(int q=1;q<=n;q++){ dp[i+1][q]=min(dp[i+1][q],dp[i][p]+dis[a[i]][q]+dis[p][a[i+1]]); dp[i+1][q]=min(dp[i+1][q],dp[i][p]+dis[p][q]+dis[q][a[i+1]]); } } } ll ans=INF; for(int i=1;i<=n;i++) ans=min(ans,dp[2*k][i]); printf("%lld ",ans); return 0; }