MYF

UVa 1347 Audiophobia

题目链接

UVa 1347

方法:DP

题目来源:紫书DP

题目分析

题目大意

平面上有n个点,按照x坐标从小到大的顺序给出,并且保证各点x坐标不相等,问一个人从左到右再从右到左,每个点只经过一次的话,所走过的最短距离是多少?

解析

当枚举一个人走的情况的话,无法保证回来的所选的点最优。所以能否两条路同时进行呢?两个人同时从左向右走,最后在最右端会和。这样可以用dp(i, j)表示当第一个人在i位置时,第二个人在j位置时的最短距离。那么还有一个问题,如果i=1, j=4,那下标为2和3的点是否访问就不得而知了,我们可以规定一下,对于dp[i][j]表示1~max(i, j)都访问过的结果,也就是说,对于dp[i][j]只可以转移到两个情况上,令i>j,则有$dp[i+1][j]=dp[i][j]+dis(i,i+1)$以及$dp[i+1][i]=dp[i][j]+dis(j,i+1)$这里第二个公式可能不太好理解,为什么是$dp[i][j]$转移到$dp[i+1][i]$而不是$dp[i][i+1]$,因为我们规定了$i>j$,也就是说,前一维的下标一定要大于后一维的下标。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <set>
#include <map>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <vector>
#include <iomanip>
#include <cstring>
#include <iostream>
#include <deque>
#include <algorithm>
#define Memset(a,val) memset(a,val,sizeof(a))
#define PI acos(-1)
#define pb push_back
#define mp make_pair
#define rt(n) (i == n ? '\n' : ' ')
#define hi printf("Hi----------\n")
#define IN freopen("input.txt","r",stdin);
#define OUT freopen("output.txt","w",stdout);
#define debug(x) cout<<"Debug : ---"<<x<<"---"<<endl;
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef pair<int,int> PII;
typedef long long ll;
const int maxn=50000+5;
const int mod=1000000007;
const int INF=0x3f3f3f3f;
const double eps=1e-8;
int x[1005],y[1005];
double dp[1005][1005];
double dis(int i,int j){
return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
int main(){
int n;
while (scanf("%d",&n)!=EOF) {
for (int i=1; i<=n; i++) {
scanf("%d%d",&x[i],&y[i]);
}

for (int i=1; i<=n; i++) {
dp[n-1][i]=dis(n-1,n)+dis(i,n); //完成两者的最后一步
}

// 从最后一步倒推前面的
for (int i=n-2; i>=2; i--) {
for (int j=1; j<i; j++) {
dp[i][j] = min(dp[i+1][j]+dis(i,i+1),dp[i+1][i]+dis(j,i+1));
}
}

printf("%.2f\n",dp[2][1]+dis(1,2));
}
}