算法竞赛入门经典 例题9-3

UVa 01347

Tour

John Doe准备去n个地方旅游,然后再回到起点。他租了一架飞机。为了省钱,他要保证每个地方只去一次,另外他打算到最远地方后才往回飞,所以需要计算出一个最短的闭环路径。

假如现在在第i个景点,那么下一次可以飞向任何一个更远的景点。虽然这一步的选择在当下没有什么影响,但是会影响到回来时的选择,所以这一步的决策应该兼顾到回来的路线。假如直接飞往第i + 2个景点,那么第i + 1个景点只能是回来时候再去了,可是应该从哪一个景点回到第i + 1个景点却不得而知,因为那都是未来才会发生的事情。

虽然不知道从哪个景点飞回来,但是知道到达第i + 1个景点后的下一站是哪里,也就是说在第i个景点时,出发的前一半路程是确定的,以及回来的后一半路程也是确定的,所以可以将回来的后一半路程也转化为出发的前一半路程,这样问题就转化为了从出发点开始,找到两条完全不同的到达最远景点的路线,使飞行距离最小。

转化之后再来找递推关系。假设在路线1(出发路线)上处于景点i,在路线2(回来路线)上处于景点j,同时假设i > j,最短距离为dist(i, j)。如果在路线1上游览景点i + 1,那么路线2还依然停留在景点j就好了;假如在路线2上游览景点i + 1,那么在路线1上依然停留在景点i,不满足i > j的关系,但是因为路线1和路线2是可交换的,所以dist(i, i + 1)dist(i + 1, i)是等价的,因此可以从dist(i, j)递推得到dist(i + 1, j)dist(i + 1, i)

如果在路线1上游览景点i + 2,那么必须要在路线2上游览景点i + 1,所以亦可以从dist(i, j)递推得到dist(i + 2, i + 1),而这个状态可以从dist(i + 1, j)得到,最终也是可以从dist(i, j)得到,所以每次只要对最近的景点进行决策即可。

最后还需要补充计算一下路线1到达终点时路线2距终点的距离,这样才是完整的闭环距离。

#include <iostream>
#include <vector>
#include <array>
#include <iomanip>
#include <cmath>
#include <cfloat>

#define EPS 1e-6

using namespace std;

struct Point
{
	int x, y;
	Point(const int x, const int y) : x(x), y(y) {}
};

bool operator<(const Point &p1, const Point &p2)
{
	return p1.x < p2.x;
}

double calDistance(const Point &p1, const Point &p2)
{
	double x = (double)(p1.x - p2.x);
	double y = (double)(p1.y - p2.y);
	return sqrt(x * x + y * y);
}

void findShortestTour(const vector<Point> &vecPoint)
{
	const size_t points = vecPoint.size();
	vector<vector<double>> distance(points, vector<double>(points, DBL_MAX));
	double go, back;
	distance[1][0] = calDistance(vecPoint[0], vecPoint[1]);
	for (size_t i = 1; i < points - 1; i++)
	{
		go = calDistance(vecPoint[i], vecPoint[i + 1]);
		for (size_t j = 0; j < i; j++)
		{
			back = calDistance(vecPoint[j], vecPoint[i + 1]);
			if (distance[i + 1][j] > distance[i][j] + go + EPS) {
				distance[i + 1][j] = distance[i][j] + go;
			}
			if (distance[i + 1][i] > distance[i][j] + back + EPS) {
				distance[i + 1][i] = distance[i][j] + back;
			}
		}
	}
	for (size_t j = 0; j < points - 1; j++)
	{
		back = calDistance(vecPoint[j], vecPoint.back());
		if (distance[points - 1][points - 1] > distance[points - 1][j] + back + EPS) {
			distance[points - 1][points - 1] = distance[points - 1][j] + back;
		}
	}
	cout << fixed << setprecision(2) <<distance[points - 1][points - 1] << endl;
}

int main()
{
	int number;
	while (cin >> number) {
		vector<Point> vecPoint;
		int x, y;
		for (int i = 0; i < number; i++)
		{
			cin >> x >> y;
			vecPoint.push_back(Point(x, y));
		}
		findShortestTour(vecPoint);
	}
	return 0;
}
/*
3
1 1
2 3
3 1
4
1 1
2 3
3 1
4 2
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值