Portal
5 4 2
1 2 1
2 3 1
3 4 1
4 5 1
1 5
2 4
5
6 10 3
1 1 6
5 6 9
3 5 8
1 4 1
2 4 7
6 6 10
1 4 2
6 5 10
3 5 2
3 1 9
1 5
2 5
4 3
28
给定一个图,再交给你 k k k个任务。每个任务的描述是给出 a i a_i ai和 b i b_i bi,要求你从当前位置开始,到 a i a_i ai取一个东西,然后送到 b i b_i bi。现要求从 1 1 1号点开始,最少经过多少路程可以完成任务。
任务期间,你可以在当前位置制造传送门,此后,你可以通过传送门无偿到达另一个传送门的位置,注意不能同时存在多于 2 2 2个的传送门。
C a s e 1 : Case1:\qquad Case1:考虑传送门的简化,若从当前这个点 a a a到达点 b b b,有一个传送门在点 s 1 s1 s1,有一个传送门在 s 2 s2 s2,那么我可以从 a → s 1 a\to s1 a→s1然后传送,再 s 2 → b s2\to b s2→b,也可以直接把 s 1 s1 s1毁掉,在 a a a建一个传送门,然后直接过去到 s 2 s2 s2即可。显然后者更优。因此传送门可以直接在当前位置建。
而删掉 s 1 s1 s1的门的操作也是可以证明是最优的。虽然我之后可能还要到点 c c c去,但是无论是从 s 2 → c s2\to c s2→c还是直接传送,前者定不会差于后者。
C a s e 2 : Case2:\qquad Case2:再考虑 d p dp dp,我们可以先考虑起始的 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k],表示我当前在 i i i点,执行 j j j号任务, k k k号位置有传送门的最小花费。但是一算,复杂度 O ( n 3 ) O(n^3) O(n3),再加上每个点要遍历每个与之相连的点,又是 O ( n ) O(n) O(n),这就爆了。因此要考虑压缩。为了压缩,要先考虑另一个优化
C a s e 3 : Case3:\qquad Case3:任务的优化,任务是给出两个点,那么我们可以把它们拆成 2 ∗ k 2*k 2∗k个终点,与原来的过程是一样的,但是这样,我们就可以把 d p dp dp中的第一维压到第二维去,第 j j j个任务的节点所在就是任务中给出的节点编号,由此,我们可以把复杂度压成 O ( n 3 ) O(n^3) O(n3)。
设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示简化后的第 i i i号任务,在 j j j有一个传送门时的最小花费。
输入时 r w [ i ] rw[i] rw[i]表示扩大后的第 i i i号任务。
d i s [ i ] [ j ] dis[i][j] dis[i][j]是 F l o y d Floyd Floyd跑出来的最短路。
首先不考虑传送门,那么有:
d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ i − 1 ] [ j ] + d i s [ r w [ i − 1 ] ] [ r w [ i ] ] ) dp[i][j]=min(dp[i][j],dp[i-1][j]+dis[rw[i-1]][rw[i]]) dp[i][j]=min(dp[i][j],dp[i−1][j]+dis[rw[i−1]][rw[i]])
再考虑传送门,遍历每个点,如果在这个点有一个传送门,设这个点是 k k k:
S i t u a t i o n 1 Situation\,1\qquad Situation1可以在当前点走到 k k k点,然后传送到 j j j点,再走到终点,所以有:
d p [ i ] [ k ] = m i n ( d p [ i ] [ k ] , d p [ i − 1 ] [ j ] + d i s [ r w [ i − 1 ] ] [ k ] + d i s [ j ] [ r w [ i ] ] ) dp[i][k]=min(dp[i][k],dp[i-1][j]+dis[rw[i-1]][k]+dis[j][rw[i]]) dp[i][k]=min(dp[i][k],dp[i−1][j]+dis[rw[i−1]][k]+dis[j][rw[i]])
S i t u a t i o n 2 Situation\,2\qquad Situation2可以在当前点建传送门,然后传送到 j j j点,走到 k k k点再走到终点,所以有:
d p [ i ] [ k ] = m i n ( d p [ i ] [ k ] , d p [ i − 1 ] [ j ] + d i s [ j ] [ k ] + d i s [ k ] [ r w [ i ] ] ) dp[i][k]=min(dp[i][k],dp[i-1][j]+dis[j][k]+dis[k][rw[i]]) dp[i][k]=min(dp[i][k],dp[i−1][j]+dis[j][k]+dis[k][rw[i]])
这样就可以覆盖所有的情况。
#include
#define ll long long
using namespace std;
const int MAXN=610;
ll dis[MAXN][MAXN],dp[MAXN][MAXN];
int rw[MAXN];
int main()
{
int n,m,u,v,s;ll w;
scanf("%d%d%d",&n,&m,&s);
memset(dis,0x3f3f3f3f,sizeof(dis));
for(int i=0;i<=n;i++) dis[i][i]=0;
for(int i=1;i<=m;i++){
scanf("%d%d%lld",&u,&v,&w);
dis[u][v]=dis[v][u]=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]);//Floyd
for(int i=1;i<=s;i++) scanf("%d%d",&rw[i*2-1],&rw[i*2]);
memset(dp,0x3f3f3f3f,sizeof(dp));rw[0]=1;
dp[0][1]=0;//没有走只能在1号位建
for(int i=1;i<=2*s;i++){
for(int j=1;j<=n;j++){
dp[i][j]=min(dp[i][j],dp[i-1][j]+dis[rw[i-1]][rw[i]]);
for(int k=1;k<=n;k++){
dp[i][k]=min(dp[i][k],dp[i-1][j]+dis[rw[i-1]][k]+dis[j][rw[i]]);
dp[i][k]=min(dp[i][k],dp[i-1][j]+dis[j][k]+dis[k][rw[i]]);
}
}
}ll ans=1ll<<60;
for(int i=1;i<=n;i++) ans=min(dp[2*s][i],ans);//枚举最后的传送门
printf("%lld",ans);
}
好强的题目。