Works Applications 2015 笔试题 Orienteering

投了一下Works Applications,结果邮件发过来两道笔试题,给了五天时间做,可以任选一道。

结果我拖到了截止那天晚上才开始做。。。第一题可以用C++,我就做了第一题。当然做的很烂了,速度完全不行的,就提交了。

这两天又改了改,耗时不再是天文数字鸟。。


题目:

Abstract

We are planning an orienteering game.
The aim of this game is to arrive at the goal (G) from the start (S) with the shortest distance.
However, the players have to pass all the checkpoints (@) on the map.
An orienteering map is to be given in the following format.

########
#@....G#
##.##@##
#..@..S#
#@.....#
########

In this problem, an orienteering map is to be given.
Calculate the minimum distance from the start to the goal with passing all the checkpoints

Specification

  • A map consists of 5 characters as following.
    You can assume that the map does not contain any invalid characters and
    the map has exactly one start symbol 'S' and exactly one goal symbol 'G'.
  • 'S' means the orienteering start.
  • 'G' means the orienteering goal.
  • '@' means an orienteering checkpoint.
  • '.' means an opened-block that players can pass.
  • '#' means a closed-block that players cannot pass.
  • It is allowed to move only by one step vertically or horizontally (up, down, left, or right) to the next block.
    Other types of movements, such as moving diagonally (left up, right up, left down and right down) and skipping one or more blocks, are NOT permitted.
  • You MUST NOT get out of the map.
  • Distance is to be defined as the number of movements to the different blocks.
  • You CAN pass opened-blocks, checkpoints, the start, and the goal more than once if necessary.
  • You can assume that parameters satisfy following conditions.
  • 1 <= width <= 100
  • 1 <= height <= 100
  • The maximum number of checkpoints is 18.
  • Return -1 if given arguments do not satisfy specifications, or players cannot arrive at the goal from the start by passing all the checkpoints.

Input

The input is to be given in the following format, from the standard input.

W H
Row1
Row2
...
RowH

The first row is to describe the width and the height of the orienteering map, sectioned by a space.
From the second row, map information is to be given for exactly the same number of rows as the height.
Each row contains exactly the same number of letters as the width.
See “Specification” for details of the symbols contained in the map.

Output

Output into the standard output, and put a return.

C++

Write the "main" function of the "Orienteering" class, included in given skeleton.
If some C++ standard libraries and members are necessary, add them to the skeleton.
You can implement them on other files, but describe them not to cause compile errors with given compile option.
Skeleton Code of C++

class Orienteering {
public:
 void main();
};
void Orienteering::main() {
 // TODO: Implement this function
}
int main(int argc, char* argv[]) {
 Orienteering o;
 o.main();
return 0;
}

Compile Options

The codes you have submitted are to be compiled by the following command.
g++ -std=c++11 -O2 -o a.out orienteering.cpp
Evaluation Criteria
This problem is to be evaluated by following criteria.

  • Being able to solve the problem correctly.
  • Being able to get the answer fast.

我的思路是,首先这大约是个走迷宫的问题。没有检查点的话应该是用A*寻路算法搞吧。我就先搞了个算两点间最短距离的函数。照着维基百科上的伪代码写了一下。维基上说A*一般用一个优先队列,我就想试试用下STL的优先队列,以前也没用过,但是发现貌似用不了,因为需要更新队列中间的元素的信息。我就用了个set。反正这块也不是速度的瓶颈。然后就把所有检查点,S,G之间的最短距离全算出来存了,供下面使用。

然后要从S通过所有检查点到G,任意两点间最短距离都算出来了算是已知的,大概就是个旅行商问题。这就成了NP问题了,所以想必检查点一多耗时就天文数字了。不过题目限制最多18个。又看维基百科,TSP问题精确解有暴力穷举,分支限界,动态规划什么的。作为一个学渣看了看DP也没看懂。。找了个网上的递归求解的代码,似懂非懂,改了改用上,算是搞出来正确结果了。

现在速度在十一二个检查点的时候就非常慢了。多一个检查点,耗时指数上升,根本搞不定18个。继续研究搞明白了,发现这个递归基本上就是用回溯的方法穷举所有排列组合。就想通过剪枝能快点,当一个分支已经经过的路径的距离大于目前的最短路径的时候就提前返回。这样快了大概不到十倍,能多算一个检查点。还是搞不定18个。

看来只能动态规划了,但我又没弄懂,所以就想着递归的时候保存计算结果,下次碰到相同的子问题直接把结果取出来就行,大概相当于DP了。最多存2^18左右大概一百万也不多。这么改了一下速度终于有了质的提高。测了一下100x100的地图18个检查点不到十秒能出结果,算是搞定了。

虽然感觉代码写得很烂,但还是贴出来算了。。

orienteering.h

#include <vector>
#include <map>

struct Point {
	int x;
	int y;
};

struct ScorePoint {
	Point p;
	int g;
	int h;
	//int f = g + h;
};

class Orienteering {
public:
	void main();
	int init();
	int solve1();
	int solve2();
	void showMap();
	int checkPoint() {return checkPoints.size();};
private:
	bool canArrive();

	int astar(const Point& start, const Point& end);

	void buildDistanceMap();

	int minDist(int n, int cur, int maxDistLeft);

	int minDist(int n, int cur);

	int width;
	int height;
	int minPath;
	Point s, g;
	std::vector<Point> checkPoints;
	std::map<long, int> distanceMap;
	std::vector<std::vector<char>> originalMap;

	std::map<long, int> minDistCache;

	bool done[18];  
};

orienteering.cpp

#include <iostream>
#include <vector>
#include <cstdlib>
#include <map>
#include <set>

#include "orienteering.h"

using namespace std;

bool operator<(const Point &a, const Point &b) {
	return a.x * 100 + a.y < b.x * 100 + b.y;
}

bool operator<(const ScorePoint &a, const ScorePoint &b) {
	return a.p < b.p;
}

bool operator==(const Point &a, const Point &b) {
	return a.x == b.x && a.y == b.y;
}

int heur(const Point& start, const Point& end){
	return abs(start.x - end.x) + abs(start.y - end.y);
}

long pathKey(const Point& start, const Point& end){
	if (start < end) {
		return pathKey(end, start);
	}
	return (start.x * 100 + start.y) * 10000 + end.x * 100 + end.y;
}

int Orienteering::init() {
	cin >> width >> height;
	if (width > 100 || height > 100) {
		return -1;
	}
	for (int i = 0; i < height; i++) {
		vector<char> line;
		for (int j = 0; j < width; j++) {
			char c;
			cin >> c;
			line.push_back(c);
			Point p = {j, i};
			switch(c) {
			case 'S':
				s = p;
				break;
			case 'G':
				g = p;
				break;
			case '@':
				checkPoints.push_back(p);
				break;
			case '.':
			case '#':
				break;
			default:
				return -1;
			}
		}
		originalMap.push_back(line);
	}
	if (checkPoints.size() > 18) {
		return -1;
	}
	return 0;
}

void Orienteering::showMap(){
	for (int i = 0; i < height; i++) {
		for (int j = 0; j < width; j++) {
			cout << originalMap[i][j];
		}
		cout << endl;
	}
}

int Orienteering::astar(const Point& start, const Point& end){
	auto iter = distanceMap.find(pathKey(start, end));
	if (iter != distanceMap.end()) {
		return iter->second;
	}

	set<ScorePoint> closeSet;
	set<ScorePoint> openSet;
	ScorePoint s;
	s.p = start;
	s.g = 0;
	s.h = heur(start, end);
	openSet.insert(s);

	while (!openSet.empty()) {
		set<ScorePoint>::iterator imin;
		int fmin = 1000;
		for (set<ScorePoint>::iterator i = openSet.begin(); i != openSet.end(); i++) {
			if (i->g + i->h < fmin) {
				fmin = i->g + i->h;
				imin = i;
			}
		}
		ScorePoint p = *imin;
		openSet.erase(imin);

		closeSet.insert(p);

		if (p.p == end) {
			distanceMap[pathKey(start, p.p)] = p.g + p.h;

			for (auto i = closeSet.begin(); i != closeSet.end(); i++) {
				int x = i->p.x;
				int y = i->p.y;
				if (originalMap[y][x] == '@' || originalMap[y][x] == 'G') {							
					distanceMap[pathKey(start, i->p)] = i->g;
				}
			}
			return p.g + p.h;
		}

		ScorePoint neighbors[4];
		neighbors[0].p.x = p.p.x + 1;
		neighbors[0].p.y = p.p.y;
		neighbors[1].p.x = p.p.x;
		neighbors[1].p.y = p.p.y + 1;
		neighbors[2].p.x = p.p.x - 1;
		neighbors[2].p.y = p.p.y;
		neighbors[3].p.x = p.p.x;
		neighbors[3].p.y = p.p.y - 1;
		for (int i = 0; i < 4; i++) {
			int x = neighbors[i].p.x;
			int y = neighbors[i].p.y;
			if (originalMap[y][x] != '#' && closeSet.find(neighbors[i]) == closeSet.end()) {
				neighbors[i].g = p.g + 1;
				neighbors[i].h = heur(neighbors[i].p, end);
				set<ScorePoint>::iterator it = openSet.find(neighbors[i]);
				if (it != openSet.end()) {
					if(neighbors[i].g < it->g) {
						openSet.erase(it);
						openSet.insert(neighbors[i]);
					}
				} else {
					openSet.insert(neighbors[i]);
				}
			}
		}
	}
	return -1;
}

bool Orienteering::canArrive() {
	if (astar(s, g) == -1)
		return false;
	for (auto i = checkPoints.begin(); i != checkPoints.end(); i++) {
		if (astar(s, *i) == -1)
			return false;
	}
	return true;
}

void Orienteering::buildDistanceMap() {
	for (int i = 0; i < checkPoints.size(); i++) {
		if (distanceMap.find(pathKey(s, checkPoints[i])) == distanceMap.end()) {
			astar(s, checkPoints[i]);
		}
	}

	for (int i = 0; i < checkPoints.size(); i++) {
		if (distanceMap.find(pathKey(g, checkPoints[i])) == distanceMap.end()) {
			astar(g, checkPoints[i]);
		}
	}

	for (int i = 0; i < checkPoints.size(); i++) {
		for (int j = 1; j < checkPoints.size(); j++) {
			if (distanceMap.find(pathKey(checkPoints[i], checkPoints[j])) == distanceMap.end()) {
				astar(checkPoints[i], checkPoints[j]);
			}
		}
	}
}

int Orienteering::minDist(int num, int cur, int maxDistLeft) {
	if (maxDistLeft <= 0) {
		return 10000;
	}

	int dist;
	if (num == 0) {
		dist = distanceMap[pathKey(g, checkPoints[cur])];
	} else {


		dist = maxDistLeft;
		for(int i = 0; i < checkPoints.size(); i++) {
			if(!done[i]) {
				done[i] = 1;
				int dist1 = distanceMap[pathKey(checkPoints[i], checkPoints[cur])];
				int dist2 = minDist(num-1, i, maxDistLeft - dist1);
				done[i] = 0;

				if (dist2 == 10000)
					continue;

				if(dist1 + dist2 < dist) {
					dist = dist1 + dist2;  
				}  
				
			}  
		}
	}

	if (dist < maxDistLeft)
		return dist;
	else
		return 10000;
}

int Orienteering::solve1() {
	if (!canArrive())
		return -1;
	if (checkPoints.empty())
		return astar(s, g);

	buildDistanceMap();
	for(int i = 0; i < checkPoints.size(); i++) {
		done[i] = 0;
	}

	minPath = 10000;
	for(int i = 0; i < checkPoints.size(); i++) {
		done[i] = 1;
		int dist1 = distanceMap[pathKey(checkPoints[i], s)];
		int dist2 = minDist(checkPoints.size() - 1, i, minPath - dist1);
		done[i] = 0;
		if (dist2 == 10000)
			continue;
		if (dist1 + dist2 < minPath)
			minPath = dist1 + dist2;
	}
	return minPath;
}

int Orienteering::minDist(int num, int cur) {
	if (num == 0) {
		return distanceMap[pathKey(g, checkPoints[cur])];
	}

	long key = 0;
	for(int i = 0; i < checkPoints.size(); i++) {
		key = (key << 1) + done[i];
	}
	key = key * 100 + cur;
	auto iter = minDistCache.find(key);
	if (iter != minDistCache.end()) {
		return iter->second;
	}

	int dist = 10000;
	for(int i = 0; i < checkPoints.size(); i++) {
		if(!done[i]) {
			done[i] = 1;
			int dist1 = distanceMap[pathKey(checkPoints[i], checkPoints[cur])];
			int dist2 = minDist(num-1, i);
			done[i] = 0;

			if(dist1 + dist2 < dist) {
				dist = dist1 + dist2;  
			}  
				
		}  
	}

	minDistCache[key] = dist;
	return dist;
}

int Orienteering::solve2() {
	if (!canArrive())
		return -1;
	if (checkPoints.empty())
		return astar(s, g);

	buildDistanceMap();
	for(int i = 0; i < checkPoints.size(); i++) {
		done[i] = 0;
	}

	minPath = 10000;
	for(int i = 0; i < checkPoints.size(); i++) {
		done[i] = 1;
		int dist1 = distanceMap[pathKey(checkPoints[i], s)];
		int dist2 = minDist(checkPoints.size() - 1, i);
		done[i] = 0;
		if (dist1 + dist2 < minPath)
			minPath = dist1 + dist2;
	}
	return minPath;
}

void Orienteering::main() {
	if (init() == -1) {
		cout << -1 << endl;
	}
	cout << solve1() << endl;
}

测试程序

#include <cstdio>
#include <ctime>
#include <iostream>
#include "orienteering.h"
using namespace std;

void test() {
	freopen("input.txt", "r", stdin);
	//freopen("output.txt", "r", stdout);
	int caseNum;
	cin >> caseNum;
	for (int i = 1; i <= caseNum; i++) {
		clock_t t = clock();

		Orienteering o;
		int result = o.init();
		int correctResult;
		cin >> correctResult;
		if (result != -1) {
			result = o.solve2();
		}

		t = clock() - t;

		if (result == correctResult) {
			cout << "Test case " << i << " passed.\tResult: " << result << ".\t" << o.checkPoint() << " checkpoint.\tTime: " << ((float)t * 1000 / CLOCKS_PER_SEC)  << "ms." << endl;
		} else {
			cout << "Test case " << i << " FAILED!"  << endl;
			cout << "Correct result: " << correctResult << ". Result: " << result << endl;
			o.showMap();
			cout << endl;
		}
	}
	//freopen("CON","r",stdin);
}

int main(int argc, char* argv[]) {
	test();
	//Orienteering o;
	//o.main();
	//system("PAUSE");
	return 0;
}

随便造的测试用例
input.txt

30

5 4
#####
#...#
#S#G#
#####
4

5 4
#####
#.#.#
#S#G#
#####
-1

5 4
#####
#.@.#
#S#G#
#####
4

5 4
#####
#G..#
#S#@#
#####
7

5 4
#####
#G.##
#S#G#
#####
-1

8 8
########
#.....S#
#.....##
#...##.#
#.#..#.#
#..#...#
#.#...G#
########
11

8 8
########
#.....S#
#.@...##
#..###.#
#.#..#@#
#..#...#
#@#...G#
########
-1

8 8
########
#S....S#
#.@...##
#..###.#
#.#..#@#
#G.#...#
#@#...G#
########
-1

8 8
########
#.....S#
#.@...##
#..###.#
#.#..#@#
#..#...#
#@#....#
########
-1

8 8
########
#.....S#
#.@...##
#..###.#
#.#..#@#
#.G#...#
#@#....#
########
-1

8 8
########
#..@@@S#
#.@...##
#...##.#
#.#..#@#
#..#...#
#.#...G#
########
15

8 8
########
#.....S#
#.@...##
#...##.#
#.#..#@#
#..#...#
#@#...G#
########
23

8 8
########
#.....S#
#.@...##
#...##.#
#G#..#@#
#..#...#
#@#....#
########
26

8 8
########
##@...S#
#..@@G##
#.######
#@######
#.##.@@#
#.@@...#
########
30

8 8
########
##@...S#
#..@@G##
#@######
#@######
#.##.@@#
#.@@...#
########
30

20 20
####################
#.....S#G.........@#
#.....#............#
#.....##.###########
#.....#............#
#..............@...#
#.....#............#
#.....#............#
#.....#@...........#
#.....##############
#.....#............#
#..................#
#@@...#............#
#########......@...#
#.....#............#
#.....#............#
#.....#............#
#.....#............#
#.....#.......@....#
####################
108

20 20
####################
#.....S#G.........@#
#.....#............#
#.....##.###########
#.....#............#
#..............@...#
#.....#............#
#.....#............#
#.....#@...........#
#.....##############
#.....#............#
#..................#
#@@...#............#
#########......@...#
#.....#........@...#
#.....#............#
#.....#............#
#.....#............#
#.....#.......@....#
####################
108

20 20
####################
#.....S#G.........@#
#.....#.....@......#
#.....##.###########
#.....#............#
#..............@...#
#.....#............#
#.....#............#
#.....#@...........#
#.....##############
#.....#............#
#..................#
#@..@.#............#
#########......@...#
#.....#........@...#
#.....#............#
#.....#............#
#.....#............#
#.....#.......@....#
####################
108

20 20
####################
#.....S#G.........@#
#.....#.....@......#
#.....##.###########
#.....#............#
#..............@...#
#.....#............#
#.....#............#
#.....#@...........#
#.....##############
#.....#............#
#........@.....@...#
#@....#............#
#########......@...#
#.....#............#
#.....#............#
#.....#............#
#.....#............#
#.....#.......@....#
####################
108

20 20
####################
#.....S#G.........@#
#.....#.....@......#
#.....##.###########
#.....#............#
#..@...........@...#
#.....#............#
#.....#............#
#.....#.....@......#
#.....##############
#.....#............#
#......@...........#
#@....#............#
#########......@...#
#.....#............#
#.....#............#
#.....#............#
#.....#............#
#.....#.......@....#
####################
108

20 20
####################
#.....S#G.........@#
#.....#.....@......#
#.....##.###########
#.....#............#
#..@@@@@@......@...#
#.....#............#
#.....#............#
#.....#.....@......#
#.....##############
#.....#............#
#......@@@@@@......#
#@....#............#
#########......@...#
#.....#............#
#.....#............#
#.....#............#
#.....#............#
#.....#.......@....#
####################
-1

20 20
####################
#.....S#G.........@#
#.....#.....@......#
#.....##.###########
#.....#............#
#..@...........@...#
#.....#............#
#.....#............#
#.....#.....@......#
#.....##############
#.....#............#
#......@@..........#
#@....#............#
#########......@...#
#.....#............#
#.....#............#
#.....#............#
#.....#............#
#.....#.......@....#
####################
108

20 20
####################
#.....S#G.........@#
#.....#.....@......#
#.....##.###########
#.....#............#
#..@...........@...#
#.....#............#
#.....#............#
#.....#.....@......#
#.....##############
#.....#............#
#......@@@.........#
#@....#............#
#########......@...#
#.....#............#
#.....#............#
#.....#............#
#.....#............#
#.....#.......@....#
####################
108

20 20
####################
#.....S#G.........@#
#.....#.....@......#
#.....##.###########
#.....#............#
#..@...........@...#
#.....#............#
#.....#............#
#.....#.....@......#
#.....##############
#.....#............#
#......@@@@........#
#@....#............#
#########......@...#
#.....#............#
#.....#............#
#.....#............#
#.....#............#
#.....#.......@....#
####################
108

20 20
####################
#.....S#G.........@#
#.....#.....@......#
#.....##.###########
#.....#............#
#..@...........@...#
#.....#............#
#.....#............#
#.....#.....@......#
#.....##############
#.....#............#
#......@@@@@.......#
#@....#............#
#########......@...#
#.....#............#
#.....#............#
#.....#............#
#.....#............#
#.....#.......@....#
####################
108

20 20
####################
#.....S#G.........@#
#.....#.....@......#
#.....##.###########
#.....#............#
#..@...........@...#
#.....#............#
#.....#............#
#.....#.....@......#
#.....##############
#.....#............#
#......@@@@@.......#
#@....#............#
#########......@...#
#.....#............#
#.....#.......@....#
#.....#............#
#.....#............#
#.....#.......@....#
####################
108

20 20
####################
#.....S#G.........@#
#.....#.....@......#
#.....##.###########
#.....#............#
#..@...........@...#
#.....#............#
#.....#............#
#..@..#.....@......#
#.....##############
#.....#............#
#......@@@@@.......#
#@....#............#
#########......@...#
#.....#............#
#.....#.......@....#
#.....#............#
#.....#............#
#.....#.......@....#
####################
108

20 20
####################
#.....S#G.........@#
#.....#.....@......#
#.....##.###########
#.....#............#
#..@........@..@...#
#.....#............#
#.....#............#
#.....#.....@......#
#.....##############
#.....#............#
#..@...@@@@@.......#
#@....#............#
#########......@...#
#.....#............#
#.....#............#
#.....#......@.....#
#.....#............#
#.....#.......@....#
####################
108

20 20
####################
#.....S#G.........@#
#.....#.....@......#
#.....##.###########
#.....#............#
#..@....@...@..@...#
#.....#............#
#.....#............#
#.....#.....@......#
#.....##############
#.....#............#
#..@...@@@@@.......#
#@....#............#
#########......@...#
#.....#............#
#.....#............#
#.....#......@.....#
#.....#............#
#.....#.......@....#
####################
108

20 20
####################
#.....S#G.........@#
#.....#.....@......#
#.....##.###########
#.....#............#
#..@....@...@..@...#
#.....#............#
#.....#............#
#.....#.....@......#
#.....##############
#.....#............#
#..@...@@@@@.......#
#@....#............#
#########......@...#
#.....#............#
#.....#............#
#.....#......@.....#
#.....#............#
#.....#@......@....#
####################
114
posted @ 2014-10-31 21:01  kilobtye  阅读(4815)  评论(0编辑  收藏  举报