题意:
给出
n
n
n个地方,
m
m
m条路,每条路都有一个权值
v
v
v,
R
R
R个想去的地方,问怎么安排行程使花费最少。
思路:
首先我们用
F
l
o
y
d
(
O
(
n
3
)
)
Floyd(O(n^3))
Floyd(O(n3))求每两个地方的最小花费。
再因为
R
R
R的取值范围是
[
2
,
15
]
[2,15]
[2,15],我们可以用二进制位
(
0
o
r
1
)
(0or1)
(0or1)来表示某个位置是否已经参观过,例如
6
=
(
0110
)
2
6=(0110)_2
6=(0110)2,就可以表示第1个和第2个地方我们已经参观过了。
之后我们用
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]来表示
i
,
j
i,j
i,j状态下的最小花费。其中
i
i
i是上面说到的二进制表示,
j
j
j表示此刻在第
j
j
j个位置(注意dp初始化)。
现在就是状态的转移:
d
p
[
i
+
(
1
<
<
k
)
]
[
k
]
=
m
i
n
(
d
p
[
i
+
1
<
<
k
]
[
k
]
,
d
p
[
i
]
[
j
]
+
F
[
g
[
j
]
[
g
[
k
]
]
dp[i+(1<<k)][k] = min(dp[i+1<<k][k], dp[i][j]+F[g[j][g[k]]
dp[i+(1<<k)][k]=min(dp[i+1<<k][k],dp[i][j]+F[g[j][g[k]]
方程的含义就是是否要通过状态
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]达到
d
p
[
i
+
(
1
<
<
k
)
]
[
k
]
dp[i+(1<<k)][k]
dp[i+(1<<k)][k]的状态(选择花费较小的)。其中的
i
+
(
1
<
<
k
)
i+(1<<k)
i+(1<<k),i并没有经过第k个地方,当
i
+
(
1
<
<
k
)
i+(1<<k)
i+(1<<k)后第
k
k
k个位置上就将变成
1
1
1。
我们将
i
i
i在区间
[
1
,
2
r
−
1
]
[1,2^r-1]
[1,2r−1]枚举,枚举每种选择状态。
j
j
j和
k
k
k都在在区间
[
0
,
r
]
[0, r]
[0,r]上枚举。
最后取
m
a
x
(
d
p
[
(
1
<
<
r
)
−
1
]
[
i
]
)
(
i
∈
[
0
,
r
]
)
max(dp[(1<<r)-1][i])(i\in[0, r])
max(dp[(1<<r)−1][i])(i∈[0,r])
C o d e Code Code
#include<bits/stdc++.h>
using namespace std;
const int inf = 1<<23;
const int N = 20;
const int M = 210;
int g[N], F[M][M], dp[1 << 15][20];
int main() {
int n, m, r, a, b, c;
cin >> n >> m >> r;
for(int i=0; i<r; i++) cin >> g[i];
//初始化F数组
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
if(i != j) F[i][j] = inf;
}
}
//读入每条路径
for(int i=0; i<m; i++) {
cin >> a >> b >> c;
F[a][b] = min(F[a][b], c);
F[b][a] = F[a][b];
}
//floyd求两点之间最小花费
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
for(int k=1; k<=n; k++) {
F[j][k] = min(F[j][k], F[j][i] + F[i][k]);
}
}
}
//初始化dp数组
memset(dp, 0x7f, sizeof dp);
for(int i=0; i<r; i++) {
dp[1 << i][i] = 0;
}
//枚举i,j,k。状态转移
for(int i=1; i<(1 << r)-1; i++) {
for(int j=0; j<r; j++) {
if(i & (1 << j)) {
for(int k=0; k<r; k++) {
if(!(i & (1 << k)))
dp[i + (1 << k)][k] = min(dp[i + (1 << k)][k], dp[i][j] + F[g[j]][g[k]]);
}
}
}
}
//取最大值,得到答案。
int ans = inf;
for(int i=0; i<r; i++) {
ans = min(ans, dp[(1 << r)-1][i]);
}
cout << ans;
return 0;
}