题意:(先转一段描述)
这种旅程即为从最左点开始,严格地从左到右直至最右点,然后严格地从右到左直至出发点。
下图(b)显示了同样的7个点的最短双调路线。求最短路线。
图a
图b
注:在一个单位栅格上显示的平面上的七个点。 a)最短闭合路线,长度大约是24.89。这个路线不是双调的。b)相同点的集合上的最短双调闭合路线。长度大约是25.58。
这是一个算导上的思考题15-1。
很经典的Dp问题,会给出两种思路(两种不同的转移方法)
首先将给出的点排序,关键字x,重新编号,从左至右1,2,3,…,n。
定义dis[i][j],表示结点i到结点j之间的距离。
法一:
定义Dp[i][j],表示从i连到1,再从1连到j,(注意,i>j,且并没有相连。)
对于任意一个点i来说,有两种连接方法,一种是如图(a)所示,i与i-1相连,另一种呢是如图(b),i与i-1不相连。
根据双调旅程,我们知道结点n一定与n相连,那么,如果我们求的Dp[n][n-1],只需将其加上dis[n-1][n]就是最短双调闭合路线。
写出方程式:
Dp[i][j]=Dp[i-1][j]+dis[i][i-1];
Dp[i][i-1]=min(Dp[i][i-1],Dp[i-1][j]+dis[j][i]);
初始状态为Dp[2][1] = dis[2][1];
法二:
定义Dp[i][j]表示第一个人走到i,第二个人走到j,下一个需要访问的点为k,k = max(i, j) + 1;
Dp[k][j] = min(Dp[i][j] + dis[i][k]);
Dp[i][k] = min(Dp[i][j] + dis[j][k]);
Dp[2][1] = Dp[1][2] = dis[1][2];
Code:
法一:
Status | Accepted |
---|---|
Length | 1580 |
Lang | C++ 5.3.0 |
Submitted | 2017-07-17 09:56:14 |
Shared |
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int Max = 1100;
const int INF = 0x3f3f3f3f;
struct node{
int x, y;
bool operator < (const node & X) const{
return x < X.x;
}
}P[Max + 5];
int N;
double dis[Max + 5][Max + 5], Dp[Max + 5][Max + 5];
bool getint(int & num){
char c; int flg = 1; num = 0;
while((c = getchar()) < '0' || c > '9'){
if(c == '-') flg = -1;
if(c == -1) return 0;
}
while(c >= '0' && c <= '9'){
num = num * 10 + c - 48;
if((c = getchar()) == -1) return 0;
}
num *= flg;
return 1;
}
double Get_Dis(int a, int b){ return sqrt(1.0 * (P[a].x - P[b].x) * (P[a].x - P[b].x) + (P[a].y - P[b].y) * (P[a].y - P[b].y));}
int main(){
while(getint(N)){
for(int i = 1; i <= N; ++ i)
getint(P[i].x), getint(P[i].y);
sort(P + 1, P + 1 + N);
for(int i = 1; i <= N; ++ i)
for(int j = i + 1; j <= N; ++ j)
dis[j][i] = dis[i][j] = Get_Dis(i, j);
Dp[2][1] = dis[1][2];
for(int i = 3; i <= N; ++ i){
Dp[i][i - 1] = 1.0 * INF;
for(int j = 1; j < i - 1; ++ j){
Dp[i][j] = Dp[i - 1][j] + dis[i - 1][i];
Dp[i][i - 1] = min(Dp[i][i - 1], Dp[i - 1][j] + dis[i][j]);
}
}
double Ans = 1.0 * INF;
for(int i = 1; i < N; ++ i)
Ans = min(Ans, Dp[N][i] + dis[i][N]);
printf("%.2lf\n", Ans);
}
return 0;
}
法二:
Status | Accepted |
---|---|
Length | 1553 |
Lang | C++ 5.3.0 |
Submitted | 2017-07-18 17:29:12 |
Shared |
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
const int Max = 1000;
struct node{
double x, y;
bool operator < (const node & X) const{
return x < X.x;
}
}P[Max + 5];
int N;
double Dp[Max + 5][Max + 5], p[Max + 5][Max + 5];
bool getint(int & num){
char c; int flg = 1; num = 0;
while((c = getchar()) < '0' || c > '9'){
if(c == '-') flg = -1;
if(c == -1) return 0;
}
while(c >= '0' && c <= '9'){
num = num * 10 + c - 48;
if((c = getchar()) == -1) return 0;
}
num *= flg;
return 1;
}
double Get_Dis(int a, int b){
return sqrt(1.0 * (P[a].x - P[b].x) * (P[a].x - P[b].x) + (P[a].y - P[b].y) * (P[a].y - P[b].y));
}
int main(){
while(getint(N)){
for(int i = 1; i <= N; ++ i)
scanf("%lf%lf", &P[i].x, &P[i].y);
for(int i = 1; i <= N; ++ i)
for(int j = i + 1; j <= N; ++ j)
p[i][j] = p[j][i] = Get_Dis(i, j);
for(int i = 0; i <= N; ++ i)
for(int j = 0; j <= N; ++ j)
Dp[i][j] = 1e12;
Dp[1][2] = Dp[2][1] = p[1][2];
for(int i = 1; i <= N; ++ i)
for(int j = 1; j <= N; ++ j) if(i != j){
int k = max(i, j) + 1;
Dp[k][j] = min(Dp[k][j], Dp[i][j] + p[i][k]);
Dp[i][k] = min(Dp[i][k], Dp[i][j] + p[k][j]);
}
printf("%.2lf\n", Dp[N][N - 1] + p[N][N - 1]);
}
}