Portal
题目描述:
您现在在一家大工厂里。可以将工厂识别为具有
N
N
N个顶点和
M
M
M个边的图形。每个边缘都有其长度。您有
k
k
k个任务要做。第
i
i
i个任务将顶点
a
i
a_i
ai,选择一个块,然后将其发送到顶点
b
i
b_i
bi。您应该按照从
1
1
1号到k号的顺序完成任务。最初,您站在
v
e
c
t
e
x
1
vectex 1
vectex1。
你手里拿着枪。当您处于某个顶点
u
u
u时,您可以向地面上射击,然后将在顶点
u
u
u建立一个门户。当工厂中有两个门户时,假设它们分别位于
u
u
u和
v
v
v处,则可以在
u
u
u和
v
v
v之间进行免费转换(就像连接长度为
0
0
0的
u
u
u和
v
v
v的边一样)。
您的手边还有一个遥控器。它使您可以随时随地关闭门户网站(一次关闭一个门户网站,而不是一次关闭所有门户网站)。而且,最多可以有两个现有门户。因此,如果要在存在两个门户网站时创建另一个门户网站,则必须先使用控制器将其关闭,然后再创建。
您需要找到必须步行的最小距离才能完成所有
k
k
k个任务。
输入描述:
第一行包含以空格分隔的三个正整数
N
N
N,
M
M
M,
k
k
k。
接下来的m行中,每个包含三个整数
u
i
u_i
ui,
v
i
v_i
vi,
w
i
w_i
wi,表示连接顶点
u
i
u_i
ui和
v
i
v_i
vi的双向边,长度为
w
i
w_i
wi。接下来的
k
k
k行中,每个行包含两个整数
a
i
a_i
ai,
b
i
b_i
bi,指示第
i
i
i个任务的开始和结束。
1
1
1
≤
\leq
≤
N
N
N
≤
\leq
≤
300
300
300
1
1
1
≤
\leq
≤
M
M
M
≤
\leq
≤
40000
40000
40000
1
1
1
≤
\leq
≤
k
k
k
≤
\leq
≤
300
300
300
1
1
1
≤
\leq
≤
u
i
u_i
ui,
v
i
v_i
vi
≤
\leq
≤
n
n
n
0
0
0
≤
\leq
≤
w
i
w_i
wi
≤
\leq
≤
1
0
9
10 ^ 9
109
1
1
1
≤
\leq
≤
a
j
a_j
aj,
b
j
b_j
bj
≤
\leq
≤
n
n
n
该图保证已连接。
输出描述:
输出一个整数,表示最小距离。
样例:
样例输入1:
5 4 2
1 2 1
2 3 1
3 4 1
4 5 1
1 5
2 4
样例输出1:
5
说明:
Solution for sample 1: walk from 1 to 5, create portals at 2 and 4 when passing by.
And then walk from 5 to 4, then you could use portals to complete the second mission.
样例输入2:
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
样例输出2:
28
样例输入3:
6 10 3
1 1 3
3 1 1
6 2 3
1 6 10
4 1 1
3 1 2
5 6 9
5 4 10
6 3 4
3 4 4
3 5
3 6
6 5
样例输出3:
16
思路:
题目大意:从点
1
1
1出发,你要按顺序完成
k
k
k个任务,每个任务有要求的起点和终点。途中你可以在所在的位置建立一个传送门,而同时只能用两个传送门存在,如果超过两个,则必须(远程)关闭任意一个传送门。
首先明确这道题用最短路和动态规划来做,
k
k
k个任务都有起点终点,也就是说要走过
2
2
2k个点。
d
p
dp
dp数组需要记录
4
4
4个状态:当前已经完成了
i
i
i个任务,当前在
k
k
k节点,传送门位置分别在
x
x
x,
y
y
y,也就是
d
p
[
i
]
[
k
]
[
x
]
[
y
]
dp[i][k][x][y]
dp[i][k][x][y]------标准的暴力写法!
但是在途中你可以在你所在位置建立一个传送门,所以只需要记录远一点的那一个就行了,所以dp数组的维度就变成了三维:
d
p
[
i
]
[
k
]
[
y
]
dp[i][k][y]
dp[i][k][y]
而题目说了输入的节点是按
1
1
1~
N
N
N顺序的,所以不用记录当前节点的位置,于是dp数组的维度就变成了二维:
d
p
[
i
]
[
y
]
dp[i][y]
dp[i][y]
根据以上信息,我们可以写出状态转移的方程式:
1.
1.
1.从
a
i
a_i
ai直接走到
a
i
+
1
a_{i+1}
ai+1
2.
2.
2.枚举
a
i
+
1
a_{i+1}
ai+1后放传送门的位置:从
a
i
a_i
ai走到放传送门的位置,然后传送到另一个传送门,走到
a
i
+
1
a_{i+1}
ai+1
3.
3.
3.直接传送到另一个传送门,在从另一个传送门走到放传送门的位置,再走到
a
i
+
1
a_{i+1}
ai+1
(有点复杂)
所以我们只要在没有传送门的情况下先对所有点跑Floyd,再用动态规划求出最小距离
AC Code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll a[1005]={1},fl[1005][1005],dp[1005][1005],w,n,m,k,u,v,ans=1e18;
int main()
{
memset(fl,0x3f,sizeof(fl));
memset(dp,0x3f,sizeof(dp));
dp[0][1]=0;
scanf("%lld%lld%lld",&n,&m,&k);
k*=2;
for(int i=0;i<=n;i++)
fl[i][i]=0;
for(int i=1;i<=m;i++)
{
scanf("%lld%lld%lld",&u,&v,&w);
fl[u][v]=fl[v][u]=min(w,fl[u][v]);
}
for(int i=1;i<=k;i++)
scanf("%lld",a+i);
for(int l=1;l<=n;l++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
fl[i][j]=min(fl[i][l]+fl[l][j],fl[i][j]);//Floyd求最短路
for(int i=1;i<=k;i++)
for(int j=1;j<=n;j++)
{
dp[i][j]=min(dp[i][j],dp[i-1][j]+fl[a[i-1]][a[i]]);
for(int l=1;l<=n;l++)
dp[i][l]=min(dp[i][l],dp[i-1][j]+min(fl[a[i-1]][l]+fl[j][a[i]],fl[j][l]+fl[l][a[i]]));
}
for(int i=1;i<=n;i++)
ans=min(ans,dp[k][i]);
printf("%lld\n",ans);
}