Works Applications 2015 筆試題 Orienteering

投了一下Works Applications,結果郵件發過來兩道筆試題,給了五天時間作,能夠任選一道。ios

結果我拖到了截止那天晚上纔開始作。。。第一題能夠用C++,我就作了第一題。固然作的很爛了,速度徹底不行的,就提交了。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 checkpointsui

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.this

    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.lua

Output

Output into the standard output, and put a return.spa

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++c++11

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
相關文章
相關標籤/搜索