虽然看上去需要维护两个传送门,但是其实dp状态只需要设计一个就行了,这是因为当我们使用传送门的时候,总是在当前点创建传送门,所以只
也就是只要维护另一个传送门即可
对于转移,我们首先要把任务拆分掉,因为虽然一个任务有两个位置,但是我们发现其实每一步可以看成一个任务,因为假如我在上一个任务的终点
对于当前任务a,b两个位置,我要先以最小的走到a,再以最小的走到b,这是不冲突的,因此我们把每个走动的位置当作一个任务
剩下的就是选择怎么走,要不就是硬走,要不就是选择传送门走。另外我们还需要维护如果要新设置传送门的状态。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const int N=2e5+10; ll d[510][510]; int loc[N]; ll f[1000][510]; int main(){ ios::sync_with_stdio(false); int n,m,k; cin>>n>>m>>k; int i,j; for(i=1;i<=n;i++){ for(j=1;j<=n;j++){ if(i==j) d[i][j]=0; else{ d[i][j]=1e18; } } } for(i=1;i<=m;i++){ int a,b; ll c; cin>>a>>b>>c; d[a][b]=d[b][a]=min(d[a][b],c); } for(i=1;i<=k;i++){ int a,b; cin>>a>>b; loc[i*2-1]=a; loc[i*2]=b; } for(int k=1;k<=n;k++){ for(i=1;i<=n;i++){ for(j=1;j<=n;j++) d[i][j]=min(d[i][j],d[i][k]+d[k][j]); } } for(i=0;i<=2*k;i++){ for(j=0;j<=n;j++){ f[i][j]=1e18; } } f[0][1]=0; loc[0]=1; for(i=0;i<2*k;i++){ for(j=1;j<=n;j++){ f[i+1][j]=min(f[i+1][j],f[i][j]+min(d[loc[i]][loc[i+1]],d[j][loc[i+1]])); for(int k=1;k<=n;k++){ f[i+1][k]=min(f[i+1][k],f[i][j]+min(d[loc[i]][k],d[j][k])+min(d[k][loc[i+1]],d[j][loc[i+1]])); } } } ll ans=1e18; for(i=1;i<=n;i++){ ans=min(ans,f[2*k][i]); } cout<<ans<<endl; }
dp状态的含义是,我完成当前任务i位于loc[i]处,传送门的位置在j