UVA - 1347 Tour
题目
旅行商问题描述:平面上n个点,确定一条连接各点的最短闭合旅程。
题目按照x递增顺序给出,各点x不同。
分析
动态规划
题目说从最左端走到最右端,再返回最左端。可以想象成两个人同时从最左端沿着两条不同路径走到最右端,保证每个点走到一次,则他们两个的路径相加最短就是答案。
最难的就是定义状态,状态定义的好坏直接影响题目的解决
将题目给出的点按 x 大小编号 1 到 n,
用
d
p
(
i
,
j
)
dp(i, j)
dp(i,j) 表示
1
到
m
a
x
(
i
,
j
)
1 到 max(i, j)
1到max(i,j) 的点都已经走过,而且当前位置是
(
i
,
j
)
(i, j)
(i,j) ,还需要至少多少距离。显然,最后要求的答案就是
d
p
(
1
,
1
)
dp(1,1)
dp(1,1)。
思考状态转移方程,
因为
d
p
(
i
,
j
)
=
=
d
p
(
j
,
i
)
dp(i,j) == dp(j,i)
dp(i,j)==dp(j,i),因此dp数组只需要填一半就行,不妨规定
i
>
j
i>j
i>j,下一次只能走到
i
+
1
i+1
i+1,那么
d
p
(
i
,
j
)
=
m
i
n
(
d
p
(
i
+
1
,
j
)
,
d
p
(
i
,
i
+
1
)
)
dp(i, j) = min(dp(i+1, j),dp(i, i+1))
dp(i,j)=min(dp(i+1,j),dp(i,i+1))
因为
d
p
(
i
,
i
+
1
)
=
=
d
p
(
i
+
1
,
i
)
dp(i,i+1) == dp(i+1, i)
dp(i,i+1)==dp(i+1,i)
方程变为
d
p
(
i
,
j
)
=
m
i
n
(
d
p
(
i
+
1
,
j
)
,
d
p
(
i
+
1
,
i
)
)
dp(i, j) = min(dp(i+1, j),dp(i+1, i))
dp(i,j)=min(dp(i+1,j),dp(i+1,i))
递推
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 1e3 + 10;
int n;
double dp[N][N]; //表示 1~max(i,j) 已经走过,现在在位置(i,j),最少还要多少距离
double x[N], y[N], dis[N][N];
int main()
{
while(cin >> n){
for(int i = 1; i <= n; i++){
scanf("%lf%lf", &x[i], &y[i]);
}
for(int i = 1; i <= n; i++){ //预处理所有点之间距离
for (int j = 1; j <= n; j++){
dis[i][j] = sqrt((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]));
}
}
for (int i = n - 1; i >= 1; i--){
for (int j = 1; j <= i; j++){
if(i == n-1){
dp[i][j] = dis[i][n] + dis[j][n]; //边界 两人直接走到终点
}else{
dp[i][j] = min(dis[i][i + 1] + dp[i + 1][j], dis[j][i + 1] + dp[i + 1][i]);
}
}
}
printf("%.2f\n", dp[1][1]);
}
return 0;
}
记忆化搜索
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 1e3 + 10;
int n;
double dp[N][N]; //表示 1~max(i,j) 已经走过,现在在位置(i,j),最少还要多少距离
double x[N], y[N], dis[N][N];
double DP(int i, int j)
{
double &ans = dp[i][j];
if(ans > 0) //已经求过
return ans;
if(i == n-1){
ans = dis[n - 1][n] + dis[j][n];
}else{
ans = min(dis[i][i + 1] + DP(i + 1, j), dis[j][i + 1] + DP(i + 1, i));
}
return ans;
}
int main()
{
while(cin >> n){
for(int i = 1; i <= n; i++){
scanf("%lf%lf", &x[i], &y[i]);
}
for(int i = 1; i <= n; i++){ //预处理所有点之间距离
for (int j = 1; j <= n; j++){
dp[i][j] = -1.0;
dis[i][j] = sqrt((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]));
}
}
printf("%.2f\n", DP(1, 1));
}
return 0;
}