Để Vectory! - Giải đua xe đua Grand Prix


39

Người dùng RugPython đã đăng một bài mới về vấn đề này, điều này tập trung nhiều hơn vào các giải pháp heuristic, do không gian tìm kiếm tăng lên. Cá nhân tôi nghĩ rằng thử thách đó đẹp hơn tôi rất nhiều, vì vậy hãy cân nhắc thử thách đó!

Vector racing là một trò chơi gây nghiện có thể được chơi bằng bút và một tờ giấy vuông. Bạn vẽ một đường đua tùy ý trên tờ giấy, xác định điểm bắt đầu và kết thúc và sau đó bạn điều khiển chiếc xe có kích thước điểm của mình theo cách rẽ. Đi đến cuối cùng nhanh nhất có thể, nhưng hãy cẩn thận để không kết thúc trong một bức tường!

Lần vết

  • Bản đồ là một lưới hai chiều, trong đó mỗi ô có tọa độ nguyên.
  • Bạn di chuyển trên các ô lưới.
  • Mỗi ô lưới là một phần của đường đua hoặc là một bức tường.
  • Chính xác một ô theo dõi là tọa độ bắt đầu.
  • Ít nhất một ô theo dõi được chỉ định là mục tiêu. Hạ cánh trên bất kỳ trong số này hoàn thành cuộc đua. Nhiều ô mục tiêu không nhất thiết phải được kết nối.

Chỉ đạo xe

Xe của bạn bắt đầu tại một tọa độ nhất định và với véc tơ vận tốc (0, 0). Trong mỗi lượt, bạn có thể điều chỉnh từng thành phần của vận tốc bằng ±1hoặc để nguyên như vậy. Sau đó, vectơ vận tốc kết quả được thêm vào vị trí xe của bạn.

Một hình ảnh có thể giúp! Vòng tròn màu đỏ là vị trí cuối cùng của bạn. Vòng tròn màu xanh là vị trí hiện tại của bạn. Vận tốc của bạn là vectơ từ vòng tròn màu đỏ sang màu xanh. Trong lượt này, tùy thuộc vào cách bạn điều chỉnh vận tốc của mình, bạn có thể di chuyển đến bất kỳ vòng tròn màu xanh lá cây nào.

                                    nhập mô tả hình ảnh ở đây

Nếu bạn hạ cánh trong một bức tường, bạn mất ngay lập tức.

Nhiệm vụ của bạn

Bạn đoán nó: viết một chương trình , đưa ra một đường đua làm đầu vào, điều khiển chiếc xe đến một trong những ô mục tiêu trong càng ít lượt càng tốt. Giải pháp của bạn sẽ có thể xử lý hợp lý tốt với các rãnh tùy ý và không được tối ưu hóa cụ thể đối với các trường hợp thử nghiệm được cung cấp.

Đầu vào

Khi chương trình của bạn được gọi, hãy đọc từ stdin :

target
n m
[ASCII representation of an n x m racetrack]
time

targetlà số lượt tối đa bạn có thể thực hiện để hoàn thành bản nhạc và timelà tổng ngân sách thời gian của bạn cho bản nhạc, tính bằng giây (không nhất thiết là số nguyên). Xem bên dưới để biết chi tiết về thời gian.

Đối với bản nhạc được phân tách bằng dòng mới, các ký tự sau được sử dụng:

  • # - Tường
  • S- sự khởi đầu
  • *- một mục tiêu
  • . - tất cả các ô theo dõi khác (tức là đường)

Tất cả các ô bên ngoài n x mlưới được cung cấp được ngụ ý là các bức tường.

Nguồn gốc tọa độ nằm ở góc trên cùng bên trái.

Đây là một ví dụ đơn giản:

8
4.0
9 6
###...***
###...***
###...***
......###
S.....###
......###

Sử dụng lập chỉ mục dựa trên 0, tọa độ bắt đầu sẽ là (0,4).

Sau mỗi lần di chuyển, bạn sẽ nhận được thêm thông tin đầu vào:

x y
u v
time

Trong trường hợp x, y, u, vlà tất cả các số nguyên 0-based. (x,y)là vị trí hiện tại của bạn và (u,v)là vận tốc hiện tại của bạn. Lưu ý rằng x+uvà / hoặc y+vcó thể nằm ngoài giới hạn.

timelà bất cứ điều gì còn lại của ngân sách thời gian của bạn, tính bằng giây. Hãy bỏ qua điều này. Điều này chỉ dành cho những người tham gia thực sự muốn đưa việc thực hiện của họ đến thời gian giới hạn.

Khi trò chơi kết thúc (vì bạn đã rơi vào một bức tường, vượt ra khỏi giới hạn, vượt quá targetlượt, hết thời gian hoặc đạt được mục tiêu), bạn sẽ nhận được một dòng trống.

Đầu ra

Đối với mỗi lượt, viết vào thiết bị xuất chuẩn :

Δu Δv

nơi ΔuΔvtừng là một trong những -1, 0, 1. Điều này sẽ được thêm vào (u,v)để xác định vị trí mới của bạn. Chỉ cần làm rõ, các hướng dẫn như sau

(-1,-1) ( 0,-1) ( 1,-1)
(-1, 0) ( 0, 0) ( 1, 0)
(-1, 1) ( 0, 1) ( 1, 1)

Một giải pháp tối ưu cho ví dụ trên sẽ là

1 0
1 -1
1 0

Lưu ý rằng bộ điều khiển không tự gắn vào stderr , vì vậy bạn có thể sử dụng nó cho bất kỳ loại đầu ra gỡ lỗi nào bạn có thể cần trong khi phát triển bot của mình. Vui lòng xóa / nhận xét bất kỳ đầu ra như vậy trong mã gửi của bạn, mặc dù.

Bot của bạn có thể mất nửa giây để phản hồi trong mỗi lượt. Đối với các lượt chơi mất nhiều thời gian hơn, bạn sẽ có ngân sách thời gian (mỗi lần theo dõi) target/2giây. Mỗi khi một lượt chơi mất hơn nửa giây, thời gian bổ sung sẽ được trừ vào ngân sách thời gian của bạn. Khi ngân sách thời gian của bạn bằng không, cuộc đua hiện tại sẽ bị hủy bỏ.

Mới: Vì lý do thực tế, tôi phải đặt giới hạn bộ nhớ (vì bộ nhớ dường như hạn chế hơn thời gian cho kích thước theo dõi hợp lý). Do đó, tôi sẽ phải hủy bỏ mọi lần chạy thử trong đó tay đua sử dụng hơn 1GB bộ nhớ được đo bởi Process ExplorerPrivate Byte .

Chấm điểm

Có một điểm chuẩn của 20 bài hát. Đối với mỗi bản nhạc:

  • Nếu bạn hoàn thành bản nhạc, điểm của bạn là số lần bạn cần để đạt được một ô mục tiêu chia chotarget .
  • Nếu bạn hết thời gian / bộ nhớ hoặc không đạt được mục tiêu trước khi targetlượt chơi trôi qua hoặc bạn rơi vào tường / ngoài giới hạn bất cứ lúc nào, điểm số của bạn là 2.
  • Nếu chương trình của bạn không mang tính quyết định, điểm của bạn là trung bình trên 10 lần chạy trên bản nhạc đó (vui lòng nêu điều này trong câu trả lời của bạn).

Điểm tổng thể của bạn là tổng của điểm số theo dõi cá nhân. Điểm số thấp nhất chiến thắng!

Hơn nữa, mọi người tham gia có thể (và được khuyến khích mạnh mẽ) cung cấp một bản nhạc chuẩn bổ sung , sẽ được thêm vào danh sách chính thức. Các câu trả lời trước sẽ được đánh giá lại bao gồm cả bài hát mới này. Điều này là để đảm bảo rằng không có giải pháp nào được điều chỉnh quá sát với các trường hợp thử nghiệm hiện có và để giải thích cho các trường hợp cạnh thú vị mà tôi có thể đã bỏ lỡ (nhưng bạn có thể phát hiện ra).

Cà vạt

Bây giờ đã có một giải pháp tối ưu, đây có thể sẽ là yếu tố chính cho điểm số của người tham gia.

Nếu có một cà vạt (do một số câu trả lời giải quyết tối ưu tất cả các bản nhạc hoặc cách khác), tôi sẽ thêm các trường hợp kiểm tra bổ sung (lớn hơn) để phá vỡ cà vạt. Để tránh bất kỳ sự thiên vị nào của con người khi tạo ra các bộ ngắt kết nối này, chúng sẽ được tạo theo cách cố định:

  • Tôi sẽ làm tăng chiều dài mặt nbằng 10so với các ca khúc cuối cùng được tạo theo cách này. (Tôi có thể bỏ qua các kích cỡ nếu chúng không phá vỡ cà vạt.)
  • Cơ sở là đồ họa vector này
  • Điều này sẽ được rasterized ở độ phân giải mong muốn bằng cách sử dụng đoạn Mathicala này .
  • Bắt đầu là ở góc trên cùng bên trái. Cụ thể, nó sẽ là ô ngoài cùng bên trái của hàng trên cùng của phần cuối của bản nhạc.
  • Mục tiêu là ở góc dưới bên phải. Cụ thể, nó sẽ là ô bên phải nhất của hàng dưới cùng của phần cuối của bản nhạc.
  • Ý targetchí 4*n.

Bản nhạc cuối cùng của điểm chuẩn ban đầu đã được tạo như thế này, với n = 50.

Bộ điều khiển

Chương trình kiểm tra các bài nộp được viết bằng Ruby và có thể được tìm thấy trên GitHub cùng với tệp điểm chuẩn tôi sẽ sử dụng. Ngoài ra còn có một bot ví dụ được gọi randomracer.rbtrong đó, chỉ đơn giản là chọn các bước di chuyển ngẫu nhiên. Bạn có thể sử dụng cấu trúc cơ bản của nó làm điểm khởi đầu cho bot của bạn để xem cách thức hoạt động của giao tiếp.

Bạn có thể chạy bot của riêng bạn dựa vào tệp theo dõi bạn chọn như sau:

ruby controller.rb track_file_name command to run your racer

ví dụ

ruby controller.rb benchmark.txt ruby randomracer.rb

Kho lưu trữ cũng chứa hai lớp Point2DTrack. Nếu bài nộp của bạn được viết bằng Ruby, vui lòng sử dụng lại chúng để thuận tiện cho bạn.

Công tắc dòng lệnh

Bạn có thể thêm dòng lệnh chuyển đổi -v, -s, -ttrước tên file của chuẩn mực. Nếu bạn muốn sử dụng nhiều thiết bị chuyển mạch, bạn cũng có thể làm, ví dụ , -vs. Đây là những gì họ làm:

-v (verbose): Sử dụng điều này để tạo ra một chút đầu ra gỡ lỗi từ bộ điều khiển.

-s (im lặng): Nếu bạn muốn theo dõi vị trí và vận tốc của mình và không quan tâm đến ngân sách thời gian, bạn có thể tắt ba dòng đầu ra đó mỗi lượt (gửi đến bài nộp của bạn) bằng cờ này.

-t(bản nhạc): Cho phép bạn chọn từng bản nhạc để kiểm tra. Ví dụ: -t "1,2,5..8,15"chỉ kiểm tra các bài hát 1, 2, 5, 6, 7, 8 và 15. Cảm ơn rất nhiều cho Ventero về tính năng này và trình phân tích cú pháp tùy chọn.

Bài nộp của bạn

Tóm lại, vui lòng bao gồm những điều sau đây trong câu trả lời của bạn:

  • Điểm số của bạn.
  • Nếu bạn đang sử dụng tính ngẫu nhiên, vui lòng nêu rõ điều này để tôi có thể tính điểm trung bình của bạn qua nhiều lần chạy.
  • Mã cho trình của bạn.
  • Vị trí của trình biên dịch hoặc trình thông dịch miễn phí cho ngôn ngữ bạn chọn chạy trên máy Windows 8.
  • Hướng dẫn biên soạn nếu cần thiết.
  • Một chuỗi dòng lệnh Windows để chạy trình của bạn.
  • Cho dù trình của bạn yêu cầu -scờ hay không.
  • (tùy chọn) Một bản nhạc mới, có thể giải được sẽ được thêm vào điểm chuẩn. Tôi sẽ xác định hợp lý targetcho theo dõi của bạn bằng tay. Khi bản nhạc được thêm vào điểm chuẩn, tôi sẽ chỉnh sửa nó khỏi câu trả lời của bạn. Tôi bảo lưu quyền yêu cầu bạn theo dõi một bản nhạc khác (chỉ trong trường hợp bạn thêm một bản nhạc lớn không cân xứng, bao gồm nghệ thuật ASCII tục tĩu trong bản nhạc, v.v.). Khi tôi thêm trường hợp kiểm tra vào tập hợp điểm chuẩn, tôi sẽ thay thế bài hát trong câu trả lời của bạn bằng một liên kết đến bài hát trong tệp điểm chuẩn để giảm sự lộn xộn trong bài đăng này.

Như bạn có thể thấy, tôi sẽ kiểm tra tất cả các lần gửi trên máy Windows 8. Nếu hoàn toàn không có cách nào để trình của bạn chạy trên Windows, tôi cũng có thể thử trên máy ảo Ubuntu. Điều này sẽ chậm hơn đáng kể, vì vậy nếu bạn muốn tối đa hóa giới hạn thời gian, hãy đảm bảo chương trình của bạn chạy trên Windows.

Có thể trình điều khiển tốt nhất xuất hiện véc tơ!

Nhưng tôi muốn chơi!

Nếu bạn muốn tự mình thử trò chơi để cảm nhận rõ hơn về nó, thì đây là cách thực hiện . Các quy tắc được sử dụng ở đó hơi phức tạp hơn một chút, nhưng nó tương tự đủ để hữu ích, tôi nghĩ vậy.

Bảng xếp hạng

Cập nhật lần cuối: 01/09/2014, 21:29
Các bản nhạc UTC trong điểm chuẩn: 25
Kích cỡ bộ ngắt: 290, 440

  1. 6.86688 - Kuroi Neko
  2. 8.73108 - user2357112 - đệ trình lần 2
  3. 9.86627 - nneonneo
  4. 10.66109 - user2357112 - đệ trình đầu tiên
  5. 12.49643 - Tia
  6. 40.0759 - bút danh 117 (xác suất)

Kết quả kiểm tra chi tiết . (Điểm cho bài nộp xác suất đã được xác định riêng.)

Câu trả lời:


5

C ++ 11 - 6,66109

Một chiều rộng khác thực hiện tìm kiếm đầu tiên, chỉ được tối ưu hóa.

Nó phải được chạy với tùy chọn -s .
Đầu vào của nó hoàn toàn không được khử trùng, vì vậy các bản nhạc sai có thể biến nó thành một quả bí ngô.

Tôi đã thử nghiệm nó với Microsoft Visual C ++ 2013, phát hành bản dựng với cờ mặc định / O2 (tối ưu hóa tốc độ).
KHÔNG xây dựng OK với g ++ và Microsoft IDE.
Công cụ cấp phát bộ nhớ barebone của tôi là một thứ nhảm nhí, vì vậy đừng hy vọng nó hoạt động với các triển khai STL khác unordered_set!

#include <cstdint>
#include <iostream>
#include <fstream>
#include <sstream>
#include <queue>
#include <unordered_set>

#define MAP_START 'S'
#define MAP_WALL  '#'
#define MAP_GOAL  '*'

#define NODE_CHUNK_SIZE   100 // increasing this will not improve performances
#define VISIT_CHUNK_SIZE 1024 // increasing this will slightly reduce mem consumption at the (slight) cost of speed

#define HASH_POS_BITS 8 // number of bits for one coordinate
#define HASH_SPD_BITS (sizeof(size_t)*8/2-HASH_POS_BITS)

typedef int32_t tCoord; // 32 bits required to overcome the 100.000 cells (insanely) long challenge

// basic vector arithmetics
struct tPoint {
    tCoord x, y;
    tPoint(tCoord x = 0, tCoord y = 0) : x(x), y(y) {}
    tPoint operator+ (const tPoint & p) { return tPoint(x + p.x, y + p.y); }
    tPoint operator- (const tPoint & p) { return tPoint(x - p.x, y - p.y); }
    bool operator== (const tPoint & p) const { return p.x == x && p.y == y;  }
};

// a barebone block allocator. Improves speed by about 30%
template <class T, size_t SIZE> class tAllocator
{
    T * chunk;
    size_t i_alloc;
    size_t m_alloc;
public:
    typedef T                 value_type;
    typedef value_type*       pointer;
    typedef const value_type* const_pointer;
    typedef std::size_t       size_type;
    typedef value_type&       reference;
    typedef const value_type& const_reference;
    tAllocator()                                              { m_alloc = i_alloc = SIZE; }
    template <class U> tAllocator(const tAllocator<U, SIZE>&) { m_alloc = i_alloc = SIZE; }
    template <class U> struct rebind { typedef tAllocator<U, SIZE> other; };
    pointer allocate(size_type n, const_pointer = 0)
    {
        if (n > m_alloc) { i_alloc = m_alloc = n; }      // grow max size if request exceeds capacity
        if ((i_alloc + n) > m_alloc) i_alloc = m_alloc;  // dump current chunk if not enough room available
        if (i_alloc == m_alloc) { chunk = new T[m_alloc]; i_alloc = 0; } // allocate new chunk when needed
        T * mem = &chunk[i_alloc];
        i_alloc += n;
        return mem;
    }
    void deallocate(pointer, size_type) { /* memory is NOT released until process exits */ }
    void construct(pointer p, const value_type& x) { new(p)value_type(x); }
    void destroy(pointer p) { p->~value_type(); }
};

// a node in our search graph
class tNode {
    static tAllocator<tNode, NODE_CHUNK_SIZE> mem; // about 10% speed gain over a basic allocation
    tNode * parent;
public:
    tPoint pos;
    tPoint speed;
    static tNode * alloc (tPoint pos, tPoint speed, tNode * parent) { return new (mem.allocate(1)) tNode(pos, speed, parent); }
    tNode (tPoint pos = tPoint(), tPoint speed = tPoint(), tNode * parent = nullptr) : parent(parent), pos(pos), speed(speed) {}
    bool operator== (const tNode& n) const { return n.pos == pos && n.speed == speed; }
    void output(void)
    {
        std::string output;
        tPoint v = this->speed;
        for (tNode * n = this->parent ; n != nullptr ; n = n->parent)
        {
            tPoint a = v - n->speed;
            v = n->speed;
            std::ostringstream ss;  // a bit of shitty c++ text I/O to print elements in reverse order
            ss << a.x << ' ' << a.y << '\n';
            output = ss.str() + output;
        }
        std::cout << output;
    }
};
tAllocator<tNode, NODE_CHUNK_SIZE> tNode::mem;

// node queueing and storing
static int num_nodes = 0;
class tNodeJanitor {
    // set of already visited nodes. Block allocator improves speed by about 20%
    struct Hasher { size_t operator() (tNode * const n) const 
    {
        int64_t hash = // efficient hashing is the key of performances
            ((int64_t)n->pos.x   << (0 * HASH_POS_BITS))
          ^ ((int64_t)n->pos.y   << (1 * HASH_POS_BITS))
          ^ ((int64_t)n->speed.x << (2 * HASH_POS_BITS + 0 * HASH_SPD_BITS))
          ^ ((int64_t)n->speed.y << (2 * HASH_POS_BITS + 1 * HASH_SPD_BITS));
        return (size_t)((hash >> 32) ^ hash);
        //return (size_t)(hash);
    }
    };
    struct Equalizer { bool operator() (tNode * const n1, tNode * const n2) const
        { return *n1 == *n2; }};
    std::unordered_set<tNode *, Hasher, Equalizer, tAllocator<tNode *, VISIT_CHUNK_SIZE>> visited;
    std::queue<tNode *> queue; // currently explored nodes queue
public:
    bool empty(void) { return queue.empty();  }
    tNode * dequeue() { tNode * n = queue.front(); queue.pop(); return n; }
    tNode * enqueue_if_new (tPoint pos, tPoint speed = tPoint(0,0), tNode * parent = nullptr)
    {
        tNode signature (pos, speed);
        tNode * n = nullptr;
        if (visited.find (&signature) == visited.end()) // the classy way to check if an element is in a set
        {
            n = tNode::alloc(pos, speed, parent);
            queue.push(n);
            visited.insert (n);
num_nodes++;
        }
        return n;
    }
};

// map representation
class tMap {
    std::vector<char> cell;
    tPoint dim; // dimensions
public:
    void set_size(tCoord x, tCoord y) { dim = tPoint(x, y); cell.resize(x*y); }
    void set(tCoord x, tCoord y, char c) { cell[y*dim.x + x] = c; }
    char get(tPoint pos)
    {
        if (pos.x < 0 || pos.x >= dim.x || pos.y < 0 || pos.y >= dim.y) return MAP_WALL;
        return cell[pos.y*dim.x + pos.x];
    }
    void dump(void)
    {
        for (int y = 0; y != dim.y; y++)
        {
            for (int x = 0; x != dim.x; x++) fprintf(stderr, "%c", cell[y*dim.x + x]);
            fprintf(stderr, "\n");
        }
    }
};

// race manager
class tRace {
    tPoint start;
    tNodeJanitor border;
    static tPoint acceleration[9];
public:
    tMap map;
    tRace ()
    {
        int target;
        tCoord sx, sy;
        std::cin >> target >> sx >> sy;
        std::cin.ignore();
        map.set_size (sx, sy);
        std::string row;
        for (int y = 0; y != sy; y++)
        {
            std::getline(std::cin, row);
            for (int x = 0; x != sx; x++)
            {
                char c = row[x];
                if (c == MAP_START) start = tPoint(x, y);
                map.set(x, y, c);
            }
        }
    }

    // all the C++ crap above makes for a nice and compact solver
    tNode * solve(void)
    {
        tNode * initial = border.enqueue_if_new (start);
        while (!border.empty())
        {
            tNode * node = border.dequeue();
            tPoint p = node->pos;
            tPoint v = node->speed;
            for (tPoint a : acceleration)
            {
                tPoint nv = v + a;
                tPoint np = p + nv;
                char c = map.get(np);
                if (c == MAP_WALL) continue;
                if (c == MAP_GOAL) return new tNode (np, nv, node);
                border.enqueue_if_new (np, nv, node);
            }
        }
        return initial; // no solution found, will output nothing
    }
};
tPoint tRace::acceleration[] = {
    tPoint(-1,-1), tPoint(-1, 0), tPoint(-1, 1),
    tPoint( 0,-1), tPoint( 0, 0), tPoint( 0, 1),
    tPoint( 1,-1), tPoint( 1, 0), tPoint( 1, 1)};

#include <ctime>
int main(void)
{
    tRace race;
    clock_t start = clock();
    tNode * solution = race.solve();
    std::cerr << "time: " << (clock()-start)/(CLOCKS_PER_SEC/1000) << "ms nodes: " << num_nodes << std::endl;
    solution->output();
    return 0;
}

Các kết quả

 No.       Size     Target   Score     Details
-------------------------------------------------------------------------------------
  1       37 x 1        36   0.22222   Racer reached goal at ( 36, 0) in 8 turns.
  2       38 x 1        37   0.24324   Racer reached goal at ( 37, 0) in 9 turns.
  3       33 x 1        32   0.25000   Racer reached goal at ( 32, 0) in 8 turns.
  4       10 x 10       10   0.40000   Racer reached goal at ( 7, 7) in 4 turns.
  5        9 x 6         8   0.37500   Racer reached goal at ( 6, 0) in 3 turns.
  6       15 x 7        16   0.37500   Racer reached goal at ( 12, 4) in 6 turns.
  7       17 x 8        16   0.31250   Racer reached goal at ( 14, 0) in 5 turns.
  8       19 x 13       18   0.27778   Racer reached goal at ( 0, 11) in 5 turns.
  9       60 x 10      107   0.14953   Racer reached goal at ( 0, 6) in 16 turns.
 10       31 x 31      106   0.23585   Racer reached goal at ( 27, 0) in 25 turns.
 11       31 x 31      106   0.24528   Racer reached goal at ( 15, 15) in 26 turns.
 12       50 x 20       50   0.24000   Racer reached goal at ( 49, 10) in 12 turns.
 13      100 x 100    2600   0.01385   Racer reached goal at ( 50, 0) in 36 turns.
 14       79 x 63      242   0.24380   Racer reached goal at ( 3, 42) in 59 turns.
 15       26 x 1        25   0.32000   Racer reached goal at ( 25, 0) in 8 turns.
 16       17 x 1        19   0.52632   Racer reached goal at ( 16, 0) in 10 turns.
 17       50 x 1        55   0.34545   Racer reached goal at ( 23, 0) in 19 turns.
 18       10 x 7        23   0.34783   Racer reached goal at ( 1, 3) in 8 turns.
 19       55 x 55       45   0.17778   Racer reached goal at ( 50, 26) in 8 turns.
 20      101 x 100     100   0.14000   Racer reached goal at ( 99, 99) in 14 turns.
 21   100000 x 1         1   1.00000   Racer reached goal at ( 0, 0) in 1 turns.
 22       50 x 50      200   0.05500   Racer reached goal at ( 47, 46) in 11 turns.
 23      290 x 290    1160   0.16466   Racer reached goal at ( 269, 265) in 191 turns.
-------------------------------------------------------------------------------------
TOTAL SCORE:                 6.66109

Biểu diễn

Ngôn ngữ C ++ nhảm nhí đó có một sở trường để khiến bạn nhảy qua vòng chỉ để di chuyển một que diêm. Tuy nhiên, bạn có thể đánh nó để tạo mã tương đối nhanh và hiệu quả bộ nhớ.

Băm

Ở đây, chìa khóa là cung cấp một bảng băm tốt cho các nút. Đó là yếu tố chi phối cho tốc độ thực hiện.
Hai triển khai unordered_set(GNU và Microsoft) mang lại chênh lệch tốc độ thực thi 30% (có lợi cho GNU, yay!).

Sự khác biệt không thực sự đáng ngạc nhiên, những gì với các xe tải mã ẩn đằng sau unordered_set.

Vì tò mò, tôi đã làm một số thống kê về trạng thái cuối cùng của bảng băm.
Cả hai thuật toán kết thúc với tỷ lệ xô / phần tử gần như nhau, nhưng phân vùng lại khác nhau:
đối với bộ ngắt kết nối 290x290, GNU có trung bình 1,5 phần tử cho mỗi nhóm không trống, trong khi Microsoft ở mức 5,8 (!).

Có vẻ như chức năng băm của tôi không được ngẫu nhiên hóa tốt bởi Microsoft ... Tôi tự hỏi liệu những kẻ ở Redmond có thực sự điểm chuẩn STL của họ không, hoặc có thể trường hợp sử dụng của tôi chỉ ủng hộ việc triển khai GNU ...

Chắc chắn, chức năng băm của tôi không ở đâu gần tối ưu. Tôi có thể đã sử dụng cách trộn số nguyên thông thường dựa trên nhiều ca / ​​muls nhưng một hàm hiệu quả băm cần có thời gian để tính toán.

Có vẻ như số lượng truy vấn bảng băm là rất cao so với số lần chèn. Chẳng hạn, trong bộ ngắt kết nối 290x290, bạn có khoảng 3,6 triệu lần chèn cho 22,7 triệu truy vấn.
Trong bối cảnh này, một băm nhỏ tối ưu nhưng nhanh chóng mang lại hiệu suất tốt hơn.

Cấp phát bộ nhớ

Cung cấp một bộ cấp phát bộ nhớ hiệu quả đến thứ hai. Nó cải thiện hiệu suất khoảng 30%. Cho dù đó là giá trị mã crap thêm vào là tranh cãi :).

Phiên bản hiện tại sử dụng từ 40 đến 55 byte cho mỗi nút.
Dữ liệu chức năng yêu cầu 24 byte cho một nút (4 tọa độ và 2 con trỏ).
Do trường hợp kiểm tra 100.000 dòng điên rồ, tọa độ phải được lưu trữ trong 4 từ, nếu không bạn có thể đạt được 8 byte bằng cách sử dụng quần short (với giá trị tọa độ tối đa là 32767). Các byte còn lại hầu hết được sử dụng bởi bảng băm của tập hợp không có thứ tự. Điều đó có nghĩa là việc xử lý dữ liệu thực sự tiêu tốn hơn một chút so với tải trọng "hữu ích".

Và người chiến thắng là...

Trên PC của tôi dưới Win7, bộ ngắt kết nối (trường hợp 23, 290x290) được giải quyết bằng phiên bản tệ nhất (tức là Microsoft biên dịch) trong khoảng 2,2 giây, với mức tiêu thụ bộ nhớ khoảng 185 Mb.
Để so sánh, nhà lãnh đạo hiện tại (mã python của user2357112) mất hơn 30 giây và tiêu tốn khoảng 780 Mb.

Vấn đề kiểm soát

Tôi không chắc là tôi có thể viết mã trong Ruby để cứu mạng tôi.
Tuy nhiên, tôi đã phát hiện và hack hai vấn đề từ mã điều khiển:

1) đọc bản đồ track.rb

Với ruby ​​1.9.3 được cài đặt, trình đọc theo dõi sẽ kêu lên về việc shift.to_ikhông có sẵn string.lines.
Sau một thời gian dài lướt qua tài liệu Ruby trực tuyến, tôi đã từ bỏ các chuỗi và thay vào đó sử dụng một mảng trung gian, như vậy (ngay khi bắt đầu tập tin):

def initialize(string)
    @track = Array.new;
    string.lines.each do |line|
        @track.push (line.chomp)
    end

2) giết ma trong controller.rb

Như các áp phích khác đã lưu ý, đôi khi bộ điều khiển cố gắng tiêu diệt các tiến trình đã thoát. Để tránh những đầu ra lỗi đáng ghét này, tôi chỉ cần xử lý ngoại lệ, như vậy (khoảng dòng 134):

if silent
    begin # ;!;
        Process.kill('KILL', racer.pid)
    rescue Exception => e
    end

Trường hợp thử nghiệm

Để đánh bại cách tiếp cận vũ phu của những người giải BFS, đường đua tệ nhất là đối diện với bản đồ 100.000 ô: một khu vực hoàn toàn tự do với mục tiêu càng xa đầu càng tốt.

Trong ví dụ này, bản đồ 100x400 với mục tiêu ở góc trên bên trái và bắt đầu ở phía dưới bên phải.

Bản đồ này có một giải pháp trong 28 lượt, nhưng một người giải BFS sẽ khám phá hàng triệu trạng thái để tìm thấy nó (tôi đếm được 10.022.658 trạng thái đã truy cập, mất khoảng 12 giây và đạt cực đại 600 Mb!).

Với ít hơn một nửa bề mặt của bộ ngắt kết nối 290x290, nó đòi hỏi số lần truy cập nút nhiều hơn khoảng 3 lần. Mặt khác, một người giải quyết dựa trên heuristic / A * sẽ đánh bại nó một cách dễ dàng.

30
100 400
*...................................................................................................
....................................................................................................
                          < 400 lines in all >
....................................................................................................
....................................................................................................
...................................................................................................S

Phần thưởng: một phiên bản PHP tương đương (nhưng hơi kém hiệu quả)

Đây là những gì tôi bắt đầu, trước khi sự chậm chạp trong ngôn ngữ vốn có đã thuyết phục tôi sử dụng C ++.
Các bảng băm nội bộ PHP dường như không hiệu quả như Python, ít nhất là trong trường hợp cụ thể này :).

<?php

class Trace {
    static $file;
    public static $state_member;
    public static $state_target;
    static function msg ($msg)
    {
        fputs (self::$file, "$msg\n");
    }

    static function dump ($var, $msg=null)
    {
        ob_start();
        if ($msg) echo "$msg ";
        var_dump($var);
        $dump=ob_get_contents();
        ob_end_clean();
        fputs (self::$file, "$dump\n");
    }

    function init ($fname)
    {
        self::$file = fopen ($fname, "w");
    }
}
Trace::init ("racer.txt");

class Point {
    public $x;
    public $y;

    function __construct ($x=0, $y=0)
    {
        $this->x = (float)$x;
        $this->y = (float)$y;
    }

    function __toString ()
    {
        return "[$this->x $this->y]";
    }

    function add ($v)
    {
        return new Point ($this->x + $v->x, $this->y + $v->y);
    }

    function vector_to ($goal)
    {
        return new Point ($goal->x - $this->x, $goal->y - $this->y);
    }
}

class Node {
    public $posx  , $posy  ;
    public $speedx, $speedy;
    private $parent;

    public function __construct ($posx, $posy, $speedx, $speedy, $parent)
    {
        $this->posx = $posx;
        $this->posy = $posy;
        $this->speedx = $speedx;
        $this->speedy = $speedy;
        $this->parent = $parent;
    }

    public function path ()
    {
        $res = array();
        $v = new Point ($this->speedx, $this->speedy);
        for ($node = $this->parent ; $node != null ; $node = $node->parent)
        {
            $nv = new Point ($node->speedx, $node->speedy);
            $a = $nv->vector_to ($v);
            $v = new Point ($node->speedx, $node->speedy);
            array_unshift ($res, $a);
        }
        return $res;
    }
}

class Map {

    private static $target;       // maximal number of turns
    private static $time;         // time available to solve
    private static $sx, $sy;      // map dimensions
    private static $cell;         // cells of the map
    private static $start;        // starting point
    private static $acceleration; // possible acceleration values

    public static function init ()
    {
        // read map definition
        self::$target = trim(fgets(STDIN));
        list (self::$sx, self::$sy) = explode (" ", trim(fgets(STDIN)));
        self::$cell = array();
        for ($y = 0 ; $y != self::$sy ; $y++) self::$cell[] = str_split (trim(fgets(STDIN)));
        self::$time = trim(fgets(STDIN));

        // get starting point
        foreach (self::$cell as $y=>$row)
        {
            $x = array_search ("S", $row);
            if ($x !== false)
            {
                self::$start = new Point ($x, $y);
Trace::msg ("start ".self::$start);
                break;
            }
        }

        // compute possible acceleration values
        self::$acceleration = array();
        for ($x = -1 ; $x <= 1 ; $x++)
        for ($y = -1 ; $y <= 1 ; $y++)
        {
            self::$acceleration[] = new Point ($x, $y);
        }
    }

    public static function solve ()
    {
        $now = microtime(true);
        $res = array();
        $border = array (new Node (self::$start->x, self::$start->y, 0, 0, null));
        $present = array (self::$start->x." ".self::$start->y." 0 0" => 1);
        while (count ($border))
        {
if ((microtime(true) - $now) > 1)
{
Trace::msg (count($present)." nodes, ".round(memory_get_usage(true)/1024)."K");
$now = microtime(true);
}
            $node = array_shift ($border);
//Trace::msg ("node $node->pos $node->speed");
            $px = $node->posx;
            $py = $node->posy;
            $vx = $node->speedx;
            $vy = $node->speedy;
            foreach (self::$acceleration as $a)
            {
                $nvx = $vx + $a->x;
                $nvy = $vy + $a->y;
                $npx = $px + $nvx;
                $npy = $py + $nvy;
                if ($npx < 0 || $npx >= self::$sx || $npy < 0 || $npy >= self::$sy || self::$cell[$npy][$npx] == "#")
                {
//Trace::msg ("invalid position $px,$py $vx,$vy -> $npx,$npy");
                    continue;
                }
                if (self::$cell[$npy][$npx] == "*")
                {
Trace::msg ("winning position $px,$py $vx,$vy -> $npx,$npy");
                    $end = new Node ($npx, $npy, $nvx, $nvy, $node);
                    $res = $end->path ();
                    break 2;
                }
//Trace::msg ("checking $np $nv");
                $signature = "$npx $npy $nvx $nvy";
                if (isset ($present[$signature])) continue;
//Trace::msg ("*** adding $np $nv");
                $border[] = new Node ($npx, $npy, $nvx, $nvy, $node);
                $present[$signature] = 1;
            }
        }
        return $res;
    }
}

ini_set("memory_limit","1000M");
Map::init ();
$res = Map::solve();
//Trace::dump ($res);
foreach ($res as $a) echo "$a->x $a->y\n";
?>

erf ... Công cụ cấp phát barebone của tôi chỉ là một chút quá barebone. Sau đó, tôi sẽ thêm các crap cần thiết để làm cho nó hoạt động với g ++. Xin lỗi vì điều đó.

OK, đã được sửa. Phiên bản g ++ thậm chí thực sự hoạt động nhanh hơn khoảng 30%. Bây giờ nó xuất ra một số số liệu thống kê trên stderr. Hãy bình luận (từ dòng cuối cùng của nguồn). Xin lỗi một lần nữa cho sai lầm.

Được rồi, nó hoạt động ngay bây giờ và tôi đã sao chép điểm của bạn. Thật nhanh quá! :) Tôi sẽ thêm trường hợp thử nghiệm của bạn vào điểm chuẩn, nhưng tôi sẽ thay đổi mục tiêu thành 400, vì điều đó phù hợp với cách tôi xác định tất cả các mục tiêu khác (ngoại trừ bộ ngắt kết nối). Tôi sẽ cập nhật bài viết chính sau khi tôi chạy lại tất cả các bài nộp khác.
Martin Ender

Cập nhật kết quả. Không cần thiết bị ngắt kết nối, bởi vì tất cả các lần gửi khác vượt quá giới hạn bộ nhớ trên bài kiểm tra của bạn. Chúc mừng bạn! :)
Martin Ender

Cảm ơn. Trên thực tế, thử thách này đã cho tôi một cơ hội để đào sâu vào các bảng băm STL này. Mặc dù tôi ghét lòng can đảm của C ++, tôi không thể không bị giết bởi sự tò mò của mình. Meo! :).

10

C ++, 5.4 (xác định, tối ưu)

Giải pháp lập trình động. Có thể tối ưu. Rất nhanh: giải quyết tất cả 20 mẫu thử trong 0,2 giây. Nên đặc biệt nhanh trên máy 64 bit. Giả sử hội đồng quản trị ít hơn 32.000 địa điểm theo mỗi hướng (điều này hy vọng là đúng).

Tay đua này có một chút khác thường. Nó tính toán đường dẫn tối ưu ở vạch xuất phát và sau đó thực hiện đường dẫn được tính ngay lập tức. Nó bỏ qua việc kiểm soát thời gian và cho rằng nó có thể hoàn thành bước tối ưu hóa đúng hạn (điều này đúng với mọi phần cứng hiện đại hợp lý). Trên các bản đồ quá lớn, có một cơ hội nhỏ mà tay đua có thể tách ra. Nếu bạn có thể thuyết phục nó để segfault, bạn sẽ nhận được điểm brownie và tôi sẽ sửa nó để sử dụng một vòng lặp rõ ràng.

Biên dịch với g++ -O3. Có thể yêu cầu C ++ 11 (cho <unordered_map>). Để chạy, chỉ cần chạy tệp thực thi được biên dịch (không có cờ hoặc tùy chọn nào được hỗ trợ; tất cả đầu vào được lấy trên stdin).

#include <unordered_map>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>

#include <cstdint>

#define MOVES_INF (1<<30)

union state {
    struct {
        short px, py, vx, vy;
    };
    uint64_t val;
};

struct result {
    int nmoves;
    short dvx, dvy;
};

typedef std::unordered_map<uint64_t, result> cache_t;
int target, n, m;
std::vector<std::string> track;
cache_t cache;

static int solve(uint64_t val) {
    cache_t::iterator it = cache.find(val);
    if(it != cache.end())
        return it->second.nmoves;

    // prevent recursion
    result res;
    res.nmoves = MOVES_INF;
    cache[val] = res;

    state cur;
    cur.val = val;
    for(int dvx = -1; dvx <= 1; dvx++) for(int dvy = -1; dvy <= 1; dvy++) {
        state next;
        next.vx = cur.vx + dvx;
        next.vy = cur.vy + dvy;
        next.px = cur.px + next.vx;
        next.py = cur.py + next.vy;
        if(next.px < 0 || next.px >= n || next.py < 0 || next.py >= m)
            continue;
        char c = track[next.py][next.px];
        if(c == '*') {
            res.nmoves = 1;
            res.dvx = dvx;
            res.dvy = dvy;
            break;
        } else if(c == '#') {
            continue;
        } else {
            int score = solve(next.val) + 1;
            if(score < res.nmoves) {
                res.nmoves = score;
                res.dvx = dvx;
                res.dvy = dvy;
            }
        }
    }

    cache[val] = res;
    return res.nmoves;
}

bool solve_one() {
    std::string line;
    float time;

    std::cin >> target;
    // std::cin >> time; // uncomment to use "time" control
    std::cin >> n >> m;
    if(!std::cin)
        return false;
    std::cin.ignore(); // skip newline at end of "n m" line

    track.clear();
    track.reserve(m);

    for(int i=0; i<m; i++) {
        std::getline(std::cin, line);
        track.push_back(line);
    }

    cache.clear();

    state cur;
    cur.vx = cur.vy = 0;
    for(int y=0; y<m; y++) for(int x=0; x<n; x++) {
        if(track[y][x] == 'S') {
            cur.px = x;
            cur.py = y;
            break;
        }
    }

    solve(cur.val);

    int sol_len = 0;
    while(track[cur.py][cur.px] != '*') {
        cache_t::iterator it = cache.find(cur.val);
        if(it == cache.end() || it->second.nmoves >= MOVES_INF) {
            std::cerr << "Failed to solve at p=" << cur.px << "," << cur.py << " v=" << cur.vx << "," << cur.vy << std::endl;
            return true;
        }

        int dvx = it->second.dvx;
        int dvy = it->second.dvy;
        cur.vx += dvx;
        cur.vy += dvy;
        cur.px += cur.vx;
        cur.py += cur.vy;
        std::cout << dvx << " " << dvy << std::endl;
        sol_len++;
    }

    //std::cerr << "Score: " << ((float)sol_len) / target << std::endl;

    return true;
}

int main() {
    /* benchmarking: */
    //while(solve_one())
    //    ;

    /* regular running */
    solve_one();
    std::string line;
    while(std::cin) std::getline(std::cin, line);

    return 0;
}

Các kết quả

 No.    Size     Target   Score     Details
-------------------------------------------------------------------------------------
  1    37 x 1        36   0.22222   Racer reached goal at ( 36, 0) in 8 turns.
  2    38 x 1        37   0.24324   Racer reached goal at ( 37, 0) in 9 turns.
  3    33 x 1        32   0.25000   Racer reached goal at ( 32, 0) in 8 turns.
  4    10 x 10       10   0.40000   Racer reached goal at ( 7, 7) in 4 turns.
  5     9 x 6         8   0.37500   Racer reached goal at ( 6, 0) in 3 turns.
  6    15 x 7        16   0.37500   Racer reached goal at ( 12, 4) in 6 turns.
  7    17 x 8        16   0.31250   Racer reached goal at ( 15, 0) in 5 turns.
  8    19 x 13       18   0.27778   Racer reached goal at ( 1, 11) in 5 turns.
  9    60 x 10      107   0.14953   Racer reached goal at ( 2, 6) in 16 turns.
 10    31 x 31      106   0.25472   Racer reached goal at ( 28, 0) in 27 turns.
 11    31 x 31      106   0.24528   Racer reached goal at ( 15, 15) in 26 turns.
 12    50 x 20       50   0.24000   Racer reached goal at ( 49, 10) in 12 turns.
 13   100 x 100    2600   0.01385   Racer reached goal at ( 50, 0) in 36 turns.
 14    79 x 63      242   0.26860   Racer reached goal at ( 3, 42) in 65 turns.
 15    26 x 1        25   0.32000   Racer reached goal at ( 25, 0) in 8 turns.
 16    17 x 1        19   0.52632   Racer reached goal at ( 16, 0) in 10 turns.
 17    50 x 1        55   0.34545   Racer reached goal at ( 23, 0) in 19 turns.
 18    10 x 7        23   0.34783   Racer reached goal at ( 1, 3) in 8 turns.
 19    55 x 55       45   0.17778   Racer reached goal at ( 52, 26) in 8 turns.
 20    50 x 50      200   0.05500   Racer reached goal at ( 47, 46) in 11 turns.
-------------------------------------------------------------------------------------
TOTAL SCORE:              5.40009

Bản thử nghiệm mới


1
Chà, một cái gì đó như thế này đã được mong đợi khá nhiều. Không có đủ trạng thái trong câu đố để đưa ra lập trình động không khả thi. Nếu tôi nhập, tôi sẽ cần gửi bản đồ yêu cầu các chiến lược tìm kiếm phức tạp hơn để giải quyết.
user2357112 hỗ trợ Monica

Làm thế nào để tay đua của bạn thực hiện trên trường hợp thử nghiệm của bạn?
user2357112 hỗ trợ Monica

0,14 (14 di chuyển)
nneonneo

Đó là thời gian thực hiện hoặc di chuyển / mục tiêu? Nếu nó di chuyển / mục tiêu, nó sẽ hoạt động như thế nào về thời gian?
user2357112 hỗ trợ Monica

1
Tôi nghĩ rằng tôi đã tìm thấy một lỗi trong mã phòng ngừa chu kỳ. Nó giả định rằng đối với mỗi nhà nước đạt tìm kiếm từ một S nhà nước, một con đường tối ưu không thể quay trở lại S. Nó có vẻ rằng nếu một con đường tối ưu không quay trở lại S, sau đó nhà nước có thể không phải là trên một con đường tối ưu (vì chúng ta có thể chỉ cần loại bỏ vòng lặp trên đó và có một con đường ngắn hơn), và vì vậy chúng tôi không quan tâm liệu chúng tôi có nhận được kết quả quá cao cho trạng thái đó hay không. Tuy nhiên, nếu một đường dẫn tối ưu đi qua các trạng thái A và B theo thứ tự đó, nhưng lần tìm kiếm đầu tiên tìm thấy A trong khi B vẫn ở trên ngăn xếp, thì kết quả của A bị đầu độc bởi việc ngăn chặn vòng lặp.
user2357112 hỗ trợ Monica

6

Python 2 , xác định, tối ưu

Đây là tay đua của tôi. Tôi chưa kiểm tra nó trên điểm chuẩn (vẫn còn lo lắng về việc cài đặt phiên bản và trình cài đặt Ruby nào), nhưng nó sẽ giải quyết mọi thứ một cách tối ưu và trong thời gian giới hạn. Lệnh để chạy nó là python whateveryoucallthefile.py. Cần -scờ điều khiển.

# Breadth-first search.
# Future directions: bidirectional search and/or A*.

import operator
import time

acceleration_options = [(dvx, dvy) for dvx in [-1, 0, 1] for dvy in [-1, 0, 1]]

class ImpossibleRaceError(Exception): pass

def read_input(line_source=raw_input):
    # We don't use the target.
    target = int(line_source())

    width, height = map(int, line_source().split())
    input_grid = [line_source() for _ in xrange(height)]

    start = None
    for i in xrange(height):
        for j in xrange(width):
            if input_grid[i][j] == 'S':
                start = i, j
                break
        if start is not None:
            break

    walls = [[cell == '#' for cell in row] for row in input_grid]
    goals = [[cell == '*' for cell in row] for row in input_grid]

    return start, walls, goals

def default_bfs_stop_threshold(walls, goals):
    num_not_wall = sum(sum(map(operator.not_, row)) for row in walls)
    num_goals = sum(sum(row) for row in goals)
    return num_goals * num_not_wall

def bfs(start, walls, goals, stop_threshold=None):
    if stop_threshold is None:
        stop_threshold = default_bfs_stop_threshold(walls, goals)

    # State representation is (x, y, vx, vy)
    x, y = start
    initial_state = (x, y, 0, 0)
    frontier = {initial_state}
    # Visited set is tracked by a map from each state to the last move taken
    # before reaching that state.
    visited = {initial_state: None}

    while len(frontier) < stop_threshold:
        if not frontier:
            raise ImpossibleRaceError

        new_frontier = set()
        for x, y, vx, vy in frontier:
            for dvx, dvy in acceleration_options:
                new_vx, new_vy = vx+dvx, vy+dvy
                new_x, new_y = x+new_vx, y+new_vy
                new_state = (new_x, new_y, new_vx, new_vy)

                if not (0 <= new_x < len(walls) and 0 <= new_y < len(walls[0])):
                    continue
                if walls[new_x][new_y]:
                    continue
                if new_state in visited:
                    continue

                new_frontier.add(new_state)
                visited[new_state] = dvx, dvy

                if goals[new_x][new_y]:
                    return construct_path_from_bfs(new_state, visited)
        frontier = new_frontier

def construct_path_from_bfs(goal_state, best_moves):
    reversed_path = []
    current_state = goal_state
    while best_moves[current_state] is not None:
        move = best_moves[current_state]
        reversed_path.append(move)

        x, y, vx, vy = current_state
        dvx, dvy = move
        old_x, old_y = x-vx, y-vy # not old_vx or old_vy
        old_vx, old_vy = vx-dvx, vy-dvy
        current_state = (old_x, old_y, old_vx, old_vy)
    return reversed_path[::-1]

def main():
    t = time.time()

    start, walls, goals = read_input()
    path = bfs(start, walls, goals, float('inf'))
    for dvx, dvy in path:
        # I wrote the whole program with x pointing down and y pointing right.
        # Whoops. Gotta flip things for the output.
        print dvy, dvx

if __name__ == '__main__':
    main()

Sau khi kiểm tra tay đua của nneonneo (nhưng không thực sự kiểm tra nó, vì tôi cũng không có trình biên dịch C ++), tôi đã thấy rằng nó dường như thực hiện tìm kiếm gần như toàn bộ không gian trạng thái, bất kể mục tiêu gần hay ngắn đến mức nào một con đường đã được tìm thấy. Tôi cũng đã thấy rằng các quy tắc thời gian có nghĩa là xây dựng một bản đồ với một giải pháp dài, phức tạp đòi hỏi một giới hạn thời gian dài, nhàm chán. Do đó, việc gửi bản đồ của tôi khá đơn giản:

Bản thử nghiệm mới

(GitHub không thể hiển thị dòng dài. Bản nhạc là *S.......[and so on].....)


Gửi bổ sung: Python 2, tìm kiếm hai chiều

Đây là một cách tiếp cận tôi đã viết cách đây khoảng hai tháng, khi cố gắng tối ưu hóa lần gửi đầu tiên. Đối với các trường hợp thử nghiệm tồn tại vào thời điểm đó, nó không mang lại sự cải thiện, vì vậy tôi đã không gửi nó, nhưng đối với bản đồ mới của Kuroi, nó dường như chỉ bị nén lại dưới nắp bộ nhớ. Tôi vẫn hy vọng người giải quyết của Kuroi sẽ đánh bại điều này, nhưng tôi quan tâm đến cách nó giữ vững.

# Bidirectional search.
# Future directions: A*.

import operator
import time

acceleration_options = [(dvx, dvy) for dvx in [-1, 0, 1] for dvy in [-1, 0, 1]]

class ImpossibleRaceError(Exception): pass

def read_input(line_source=raw_input):
    # We don't use the target.
    target = int(line_source())

    width, height = map(int, line_source().split())
    input_grid = [line_source() for _ in xrange(height)]

    start = None
    for i in xrange(height):
        for j in xrange(width):
            if input_grid[i][j] == 'S':
                start = i, j
                break
        if start is not None:
            break

    walls = [[cell == '#' for cell in row] for row in input_grid]
    goals = [[cell == '*' for cell in row] for row in input_grid]

    return start, walls, goals

def bfs_to_bidi_threshold(walls, goals):
    num_not_wall = sum(sum(map(operator.not_, row)) for row in walls)
    num_goals = sum(sum(row) for row in goals)
    return num_goals * (num_not_wall - num_goals)

class GridBasedGoalContainer(object):
    '''Supports testing whether a state is a goal state with `in`.

    Does not perform bounds checking.'''
    def __init__(self, goal_grid):
        self.goal_grid = goal_grid
    def __contains__(self, state):
        x, y, vx, vy = state
        return self.goal_grid[x][y]

def forward_step(state, acceleration):
    x, y, vx, vy = state
    dvx, dvy = acceleration

    new_vx, new_vy = vx+dvx, vy+dvy
    new_x, new_y = x+new_vx, y+new_vy

    return (new_x, new_y, new_vx, new_vy)

def backward_step(state, acceleration):
    x, y, vx, vy = state
    dvx, dvy = acceleration

    old_x, old_y = x-vx, y-vy
    old_vx, old_vy = vx-dvx, vy-dvy

    return (old_x, old_y, old_vx, old_vy)

def bfs(start, walls, goals):
    x, y = start
    initial_state = (x, y, 0, 0)
    initial_frontier = {initial_state}
    visited = {initial_state: None}

    goal_state, frontier, visited = general_bfs(
        frontier=initial_frontier,
        visited=visited,
        walls=walls,
        goalcontainer=GridBasedGoalContainer(goals),
        stop_threshold=float('inf'),
        step_function=forward_step
    )

    return construct_path_from_bfs(goal_state, visited)

def general_bfs(
        frontier,
        visited,
        walls,
        goalcontainer,
        stop_threshold,
        step_function):

    while len(frontier) <= stop_threshold:
        if not frontier:
            raise ImpossibleRaceError

        new_frontier = set()
        for state in frontier:
            for accel in acceleration_options:
                new_state = new_x, new_y, new_vx, new_vy = \
                        step_function(state, accel)

                if not (0 <= new_x < len(walls) and 0 <= new_y < len(walls[0])):
                    continue
                if walls[new_x][new_y]:
                    continue
                if new_state in visited:
                    continue

                new_frontier.add(new_state)
                visited[new_state] = accel

                if new_state in goalcontainer:
                    return new_state, frontier, visited
        frontier = new_frontier
    return None, frontier, visited

def max_velocity_component(n):
    # It takes a distance of at least 0.5*v*(v+1) to achieve a velocity of
    # v in the x or y direction. That means the map has to be at least
    # 1 + 0.5*v*(v+1) rows or columns long to accomodate such a velocity.
    # Solving for v, we get a velocity cap as follows.
    return int((2*n-1.75)**0.5 - 0.5)

def solver(
        start,
        walls,
        goals,
        mode='bidi'):

    x, y = start
    initial_state = (x, y, 0, 0)
    initial_frontier = {initial_state}
    visited = {initial_state: None}
    if mode == 'bidi':
        stop_threshold = bfs_to_bidi_threshold(walls, goals)
    elif mode == 'bfs':
        stop_threshold = float('inf')
    else:
        raise ValueError('Unsupported search mode: {}'.format(mode))

    goal_state, frontier, visited = general_bfs(
        frontier=initial_frontier,
        visited=visited,
        walls=walls,
        goalcontainer=GridBasedGoalContainer(goals),
        stop_threshold=stop_threshold,
        step_function=forward_step
    )

    if goal_state is not None:
        return construct_path_from_bfs(goal_state, visited)

    # Switching to bidirectional search.

    not_walls_or_goals = []
    goal_list = []
    for x in xrange(len(walls)):
        for y in xrange(len(walls[0])):
            if not walls[x][y] and not goals[x][y]:
                not_walls_or_goals.append((x, y))
            if goals[x][y]:
                goal_list.append((x, y))
    max_vx = max_velocity_component(len(walls))
    max_vy = max_velocity_component(len(walls[0]))
    reverse_visited = {(goal_x, goal_y, goal_x-prev_x, goal_y-prev_y): None
                        for goal_x, goal_y in goal_list
                        for prev_x, prev_y in not_walls_or_goals
                        if abs(goal_x-prev_x) <= max_vx
                        and abs(goal_y - prev_y) <= max_vy}
    reverse_frontier = set(reverse_visited)
    while goal_state is None:
        goal_state, reverse_frontier, reverse_visited = general_bfs(
            frontier=reverse_frontier,
            visited=reverse_visited,
            walls=walls,
            goalcontainer=frontier,
            stop_threshold=len(frontier),
            step_function=backward_step
        )
        if goal_state is not None:
            break
        goal_state, frontier, visited = general_bfs(
            frontier=frontier,
            visited=visited,
            walls=walls,
            goalcontainer=reverse_frontier,
            stop_threshold=len(reverse_frontier),
            step_function=forward_step
        )
    forward_path = construct_path_from_bfs(goal_state, visited)
    backward_path = construct_path_from_bfs(goal_state,
                                            reverse_visited,
                                            step_function=forward_step)
    return forward_path + backward_path[::-1]

def construct_path_from_bfs(goal_state,
                            best_moves,
                            step_function=backward_step):
    reversed_path = []
    current_state = goal_state
    while best_moves[current_state] is not None:
        move = best_moves[current_state]
        reversed_path.append(move)
        current_state = step_function(current_state, move)
    return reversed_path[::-1]

def main():
    start, walls, goals = read_input()
    t = time.time()
    path = solver(start, walls, goals)
    for dvx, dvy in path:
        # I wrote the whole program with x pointing down and y pointing right.
        # Whoops. Gotta flip things for the output.
        print dvy, dvx

if __name__ == '__main__':
    main()

Điều này đôi khi thất bại trong trường hợp 12 và 13. Không biết tại sao vì các thông báo lỗi hơi ... không thân thiện
Ray

@Ray Tôi cũng nhận được thông báo lỗi, nhưng tôi luôn nhận được kết quả cho những thông báo đó. Tôi nghĩ rằng nó có thể là một cái gì đó trong bộ điều khiển của tôi, bởi vì có vẻ như bộ điều khiển cố gắng giết quá trình tay đua mặc dù nó đã kết thúc.
Martin Ender

@ m.buettner Tôi tìm thấy lý do, thêm -s thì sẽ ổn thôi.
Ray

@Ray Oh yeah, tôi đang làm điều đó. Tôi vẫn gặp lỗi trên các rãnh 13 và 14 khi bộ điều khiển đang cố giết quá trình mặc dù kết quả đã có. Tôi đoán tôi nên xem xét điều đó, nhưng không ảnh hưởng đến việc ghi bàn nên tôi chưa bận tâm.
Martin Ender

Thật không may, tôi đã phải thêm một quy tắc khác. Bộ nhớ dường như hạn chế hơn thời gian trong thử thách này, vì vậy tôi đã phải đặt ra một mức độ khó để giới hạn mức tiêu thụ bộ nhớ. Bất kỳ hoạt động nào trong đó tay đua của bạn sử dụng hơn 1GB bộ nhớ sẽ bị hủy bỏ để có hiệu lực tương tự như vượt quá giới hạn thời gian. Đối với nhóm bản nhạc hiện tại, điểm của bạn không bị ảnh hưởng bởi thay đổi này. (Tôi nghĩ rằng bạn sẽ đạt đến giới hạn đó đối với những người phá vỡ xung quanh n = 400.) Vui lòng cho tôi biết nếu bạn áp dụng bất kỳ tối ưu hóa nào, để tôi có thể chạy lại các bài kiểm tra.
Martin Ender

3

Python 3: 6.49643 (Tối ưu, BFS)

Đối với tệp điểm chuẩn 20 trường hợp cũ, nó có số điểm 5.35643. Giải pháp của @nneonneo không tối ưu vì nó có 5.4. Một số lỗi có thể.

Giải pháp này sử dụng BFS để tìm kiếm Biểu đồ, mỗi trạng thái tìm kiếm có dạng (x, y, dx, dy). Sau đó, tôi sử dụng bản đồ để ánh xạ từ các tiểu bang đến khoảng cách. Trong trường hợp xấu nhất, độ phức tạp của không gian và thời gian là O (n ^ 2 m ^ 2). Điều này sẽ hiếm khi xảy ra vì tốc độ sẽ không quá cao hoặc tay đua sẽ gặp nạn. Trên thực tế, máy của tôi mất 3 giây để hoàn thành tất cả 22 bản thử nghiệm.

from collections import namedtuple, deque
import itertools

Field = namedtuple('Map', 'n m grids')

class Grid:
    WALL = '#'
    EMPTY = '.'
    START = 'S'
    END = '*'

def read_nums():
    return list(map(int, input().split()))

def read_field():
    m, n = read_nums()
    return Field(n, m, [input() for i in range(n)])

def find_start_pos(field):
    return next((i, j)
        for i in range(field.n) for j in range(field.m)
        if field.grids[i][j] == Grid.START)

def can_go(field, i, j):
    return 0 <= i < field.n and 0 <= j < field.m and field.grids[i][j] != Grid.WALL

def trace_path(start, end, prev):
    if end == start:
        return
    end, step = prev[end]
    yield from trace_path(start, end, prev)
    yield step

def solve(max_turns, field, time):
    i0, j0 = find_start_pos(field)
    p0 = i0, j0, 0, 0
    prev = {}
    que = deque([p0])
    directions = list(itertools.product((-1, 0, 1), (-1, 0, 1)))

    while que:
        p = i, j, vi, vj = que.popleft()
        for dvi, dvj in directions:
            vi1, vj1 = vi + dvi, vj + dvj
            i1, j1 = i + vi1, j + vj1
            if not can_go(field, i1, j1):
                continue
            p1 = i1, j1, vi1, vj1
            if p1 in prev:
                continue
            que.append(p1)
            prev[p1] = p, (dvi, dvj)
            if field.grids[i1][j1] == Grid.END:
                return trace_path(p0, p1, prev)
    return []

def main():
    for dvy, dvx in solve(int(input()), read_field(), float(input())):
        print(dvx, dvy)

main()

# Các kết quả

± % time ruby controller.rb benchmark.txt python ../mybfs.py                                                                                                                                                                             !9349
["benchmark.txt", "python", "../mybfs.py"]

Running 'python ../mybfs.py' against benchmark.txt

 No.       Size     Target   Score     Details
-------------------------------------------------------------------------------------
  1       37 x 1        36   0.22222   Racer reached goal at ( 36, 0) in 8 turns.
  2       38 x 1        37   0.24324   Racer reached goal at ( 37, 0) in 9 turns.
  3       33 x 1        32   0.25000   Racer reached goal at ( 32, 0) in 8 turns.
  4       10 x 10       10   0.40000   Racer reached goal at ( 7, 7) in 4 turns.
  5        9 x 6         8   0.37500   Racer reached goal at ( 6, 0) in 3 turns.
  6       15 x 7        16   0.37500   Racer reached goal at ( 12, 4) in 6 turns.
  7       17 x 8        16   0.31250   Racer reached goal at ( 14, 0) in 5 turns.
  8       19 x 13       18   0.27778   Racer reached goal at ( 0, 11) in 5 turns.
  9       60 x 10      107   0.14953   Racer reached goal at ( 0, 6) in 16 turns.
 10       31 x 31      106   0.23585   Racer reached goal at ( 27, 0) in 25 turns.
 11       31 x 31      106   0.24528   Racer reached goal at ( 15, 15) in 26 turns.
 12       50 x 20       50   0.24000   Racer reached goal at ( 49, 10) in 12 turns.
 13      100 x 100    2600   0.01385   Racer reached goal at ( 50, 0) in 36 turns.
 14       79 x 63      242   0.24380   Racer reached goal at ( 3, 42) in 59 turns.
 15       26 x 1        25   0.32000   Racer reached goal at ( 25, 0) in 8 turns.
 16       17 x 1        19   0.52632   Racer reached goal at ( 16, 0) in 10 turns.
 17       50 x 1        55   0.34545   Racer reached goal at ( 23, 0) in 19 turns.
 18       10 x 7        23   0.34783   Racer reached goal at ( 1, 3) in 8 turns.
 19       55 x 55       45   0.17778   Racer reached goal at ( 50, 26) in 8 turns.
 20      101 x 100     100   0.14000   Racer reached goal at ( 99, 99) in 14 turns.
 21   100000 x 1         1   1.00000   Racer reached goal at ( 0, 0) in 1 turns.
 22       50 x 50      200   0.05500   Racer reached goal at ( 47, 46) in 11 turns.
-------------------------------------------------------------------------------------
TOTAL SCORE:                 6.49643

ruby controller.rb benchmark.txt python ../mybfs.py  3.06s user 0.06s system 99% cpu 3.146 total

Có, theo nhận xét của user2357112 có một lỗi trong phòng ngừa chu kỳ của nneonneo. Theo như tôi biết, tốc độ bị giới hạn bởi O(√n)điều đó sẽ khiến việc thực hiện của bạn O(n³)trên các ô vuông (giống như những cái khác, tôi cho là vậy). Tôi sẽ thêm một bộ ngắt kết nối để ghi điểm gửi của bạn so với user2357112 sau ngày hôm nay.
Martin Ender

Btw, bạn có kế hoạch để thêm một trường hợp thử nghiệm?
Martin Ender

@ m.buettner Không, tôi không có đủ hiểu biết cho trò chơi này. Vì vậy, testcase của tôi sẽ không phải là một thú vị.
Ray

Thật không may, tôi đã phải thêm một quy tắc khác. Bộ nhớ dường như hạn chế hơn thời gian trong thử thách này, vì vậy tôi đã phải đặt ra một mức độ khó để giới hạn mức tiêu thụ bộ nhớ. Bất kỳ hoạt động nào trong đó tay đua của bạn sử dụng hơn 1GB bộ nhớ sẽ bị hủy bỏ để có hiệu lực tương tự như vượt quá giới hạn thời gian. Với quy tắc này, nội dung gửi của bạn là lần đầu tiên vượt quá giới hạn đó đối với bộ ngắt kích thước n=270, đó là lý do tại sao bạn hiện đứng sau hai lần gửi "tối ưu" khác. Điều đó đang được nói, trình của bạn cũng là chậm nhất trong ba, vì vậy dù sao cũng sẽ là thứ ba, chỉ với một bộ ngắt kết nối lớn hơn.
Martin Ender

Xin vui lòng cho tôi biết nếu bạn áp dụng bất kỳ tối ưu hóa, vì vậy tôi có thể chạy lại các bài kiểm tra.
Martin Ender

1

RandomRacer, ~ 40.0 (trung bình trên 10 lần chạy)

Không phải là bot này không bao giờ hoàn thành một bản nhạc, nhưng chắc chắn ít thường xuyên hơn một lần trong 10 lần thử. (Tôi nhận được điểm số không tệ nhất trong mỗi 20 đến 30 mô phỏng hoặc hơn thế.)

Điều này chủ yếu là để hoạt động như một trường hợp cơ bản và để chứng minh một triển khai (Ruby) có thể cho một tay đua:

# Parse initial input
target = gets.to_i
size = gets.split.map(&:to_i)
track = []
size[1].times do
    track.push gets
end
time_budget = gets.to_f

# Find start position
start_y = track.find_index { |row| row['S'] }
start_x = track[start_y].index 'S'

position = [start_x, start_y]
velocity = [0, 0]

while true
    x = rand(3) - 1
    y = rand(3) - 1
    puts [x,y].join ' '
    $stdout.flush

    first_line = gets
    break if !first_line || first_line.chomp.empty?

    position = first_line.split.map(&:to_i)
    velocity = gets.split.map(&:to_i)
    time_budget = gets.to_f
end

Chạy nó với

ruby controller.rb benchmark.txt ruby randomracer.rb

1

Tay đua ngẫu nhiên 2.0, ~ 31

Chà, điều này sẽ không đánh bại người giải tối ưu được đăng, nhưng đó là một cải tiến nhỏ trên một tay đua ngẫu nhiên. Sự khác biệt chính là tay đua này sẽ chỉ xem xét ngẫu nhiên đi đến nơi không có tường, trừ khi nó hết chỗ hợp lệ để di chuyển, và nếu nó có thể di chuyển đến mục tiêu rẽ, nó sẽ. Nó cũng sẽ không di chuyển để ở cùng một vị trí, trừ khi không có động thái nào khác (không chắc, nhưng có thể).

Được triển khai trong Java, được biên dịch với java8, nhưng Java 6 sẽ ổn. Không có tham số dòng lệnh. Có một cụm phân cấp khá tốt, vì vậy tôi nghĩ rằng tôi đang làm java đúng.

import java.util.Scanner;
import java.util.Random;
import java.util.ArrayList;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

public class VectorRacing   {
    private static Scanner in = new Scanner(System.in);
    private static Random rand = new Random();
    private static Track track;
    private static Racer racer;
    private static int target;
    private static double time;
    public static void main(String[] args)  {
        init();
        main_loop();
    }
    private static void main_loop() {
        Scanner linescan;
        String line;
        int count = 0,
            x, y, u, v;

        while(!racer.lost() && !racer.won() && count < target)  {
            Direction d = racer.think();
            racer.move(d);
            count++;
            System.out.println(d);

            line = in.nextLine();
            if(line.equals("")) {
                break;
            }
            linescan = new Scanner(line);
            x = linescan.nextInt();
            y = linescan.nextInt();
            linescan = new Scanner(in.nextLine());
            u = linescan.nextInt();
            v = linescan.nextInt();
            time = Double.parseDouble(in.nextLine());

            assert x == racer.location.x;
            assert y == racer.location.y;
            assert u == racer.direction.x;
            assert v == racer.direction.y;
        }
    }
    private static void init()  {
        target = Integer.parseInt(in.nextLine());
        int width = in.nextInt();
        int height = Integer.parseInt(in.nextLine().trim());
        String[] ascii = new String[height];
        for(int i = 0; i < height; i++) {
            ascii[i] = in.nextLine();
        }
        time = Double.parseDouble(in.nextLine());
        track = new Track(width, height, ascii);
        for(int y = 0; y < ascii.length; y++)   {
            int x = ascii[y].indexOf("S");
            if( x != -1)    {
                racer = new RandomRacer(track, new Location(x, y));
                break;
            }
        }
    }

    public static class RandomRacer extends Racer   {
        public RandomRacer(Track t, Location l) {
            super(t, l);
        }
        public Direction think()    {
            ArrayList<Pair<Location, Direction> > possible = this.getLocationsCanMoveTo();
            if(possible.size() == 0)    {
                return Direction.NONE;
            }
            Pair<Location, Direction> ret = null;
            do  {
                ret = possible.get(rand.nextInt(possible.size()));
            }   while(possible.size() != 1 && ret.a.equals(this.location));
            return ret.b;
        }
    }

    // Base things
    public enum Direction   {
        NORTH("0 -1"), SOUTH("0 1"), EAST("1 0"), WEST("-1 0"), NONE("0 0"),
        NORTH_EAST("1 -1"), NORTH_WEST("-1 -1"), SOUTH_EAST("1 1"), SOUTH_WEST("-1 1");

        private final String d;
        private Direction(String d) {this.d = d;}
        public String toString()    {return d;}
    }
    public enum Cell    {
        WALL('#'), GOAL('*'), ROAD('.'), OUT_OF_BOUNDS('?');

        private final char c;
        private Cell(char c)    {this.c = c;}
        public String toString()    {return "" + c;}
    }

    public static class Track   {
        private Cell[][] track;
        private int target;
        private double time;
        public Track(int width, int height, String[] ascii) {
            this.track = new Cell[width][height];
            for(int y = 0; y < height; y++) {
                for(int x = 0; x < width; x++)  {
                    switch(ascii[y].charAt(x))  {
                        case '#':   this.track[x][y] = Cell.WALL; break;
                        case '*':   this.track[x][y] = Cell.GOAL; break;
                        case '.':
                        case 'S':   this.track[x][y] = Cell.ROAD; break;
                        default:    System.exit(-1);
                    }
                }
            }
        }
        public Cell atLocation(Location loc)    {
            if(loc.x < 0 || loc.x >= track.length || loc.y < 0 || loc.y >= track[0].length) return Cell.OUT_OF_BOUNDS;
            return track[loc.x][loc.y];
        }

        public String toString()    {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            PrintStream ps = new PrintStream(bos);
            for(int y = 0; y < track[0].length; y++)    {
                for(int x = 0; x < track.length; x++)   {
                    ps.append(track[x][y].toString());
                }
                ps.append('\n');
            }
            String ret = bos.toString();
            ps.close();
            return ret;
        }
    }

    public static abstract class Racer  {
        protected Velocity tdir;
        protected Location tloc;
        protected Track track;
        public Velocity direction;
        public Location location;

        public Racer(Track track, Location start)   {
            this.track = track;
            direction = new Velocity(0, 0);
            location = start;
        }
        public boolean canMove() throws GoHereDammitException {return canMove(Direction.NONE);}
        public boolean canMove(Direction d) throws GoHereDammitException    {
            tdir = new Velocity(direction);
            tloc = new Location(location);
            tdir.add(d);
            tloc.move(tdir);
            Cell at = track.atLocation(tloc);
            if(at == Cell.GOAL) {
                throw new GoHereDammitException();
            }
            return at == Cell.ROAD;
        }
        public ArrayList<Pair<Location, Direction> > getLocationsCanMoveTo()    {
            ArrayList<Pair<Location, Direction> > ret = new ArrayList<Pair<Location, Direction> >(9);
            for(Direction d: Direction.values())    {
                try {
                    if(this.canMove(d)) {
                        ret.add(new Pair<Location, Direction>(tloc, d));
                    }
                }   catch(GoHereDammitException e)  {
                    ret.clear();
                    ret.add(new Pair<Location, Direction>(tloc, d));
                    return ret;
                }
            }
            return ret;
        }
        public void move()  {move(Direction.NONE);}
        public void move(Direction d)   {
            direction.add(d);
            location.move(direction);
        }
        public boolean won()    {
            return track.atLocation(location) == Cell.GOAL;
        }
        public boolean lost()   {
            return track.atLocation(location) == Cell.WALL || track.atLocation(location) == Cell.OUT_OF_BOUNDS;
        }
        public String toString()    {
            return location + ", " + direction;
        }
        public abstract Direction think();

        public class GoHereDammitException extends Exception    {
            public GoHereDammitException()  {}
        }
    }

    public static class Location extends Point  {
        public Location(int x, int y)   {
            super(x, y);
        }
        public Location(Location l) {
            super(l);
        }
        public void move(Velocity d)    {
            this.x += d.x;
            this.y += d.y;
        }
    }

    public static class Velocity extends Point  {
        public Velocity(int x, int y)   {
            super(x, y);
        }
        public Velocity(Velocity v) {
            super(v);
        }
        public void add(Direction d)    {
            if(d == Direction.NONE) return;
            if(d == Direction.NORTH || d == Direction.NORTH_EAST || d == Direction.NORTH_WEST)  this.y--;
            if(d == Direction.SOUTH || d == Direction.SOUTH_EAST || d == Direction.SOUTH_WEST)  this.y++;
            if(d == Direction.EAST || d == Direction.NORTH_EAST || d == Direction.SOUTH_EAST)   this.x++;
            if(d == Direction.WEST || d == Direction.NORTH_WEST || d == Direction.SOUTH_WEST)   this.x--;
        }
    }

    public static class Point   {
        protected int x, y;
        protected Point(int x, int y)   {
            this.x = x;
            this.y = y;
        }
        protected Point(Point c)    {
            this.x = c.x;
            this.y = c.y;
        }
        public int getX()   {return x;}
        public int getY()   {return y;}
        public String toString()    {return "(" + x + ", " + y + ")";}
        public boolean equals(Point p)  {
            return this.x == p.x && this.y == p.y;
        }
    }

    public static class Pair<T, U>  {
        public T a;
        public U b;
        public Pair(T t, U u)   {
            a=t;b=u;
        }
    }
}

Kết quả (Trường hợp tốt nhất tôi từng thấy)

Running 'java VectorRacing' against ruby-runner/benchmark.txt

 No.    Size     Target   Score     Details
-------------------------------------------------------------------------------------
  1    37 x 1        36   0.38889   Racer reached goal at ( 36, 0) in 14 turns.
  2    38 x 1        37   0.54054   Racer reached goal at ( 37, 0) in 20 turns.
  3    33 x 1        32   0.62500   Racer reached goal at ( 32, 0) in 20 turns.
  4    10 x 10       10   0.40000   Racer reached goal at ( 9, 8) in 4 turns.
  5     9 x 6         8   0.75000   Racer reached goal at ( 6, 2) in 6 turns.
  6    15 x 7        16   2.00000   Racer did not reach the goal within 16 turns.
  7    17 x 8        16   2.00000   Racer hit a wall at position ( 8, 2).
  8    19 x 13       18   0.44444   Racer reached goal at ( 16, 2) in 8 turns.
  9    60 x 10      107   0.65421   Racer reached goal at ( 0, 6) in 70 turns.
 10    31 x 31      106   2.00000   Racer hit a wall at position ( 25, 9).
 11    31 x 31      106   2.00000   Racer hit a wall at position ( 8, 1).
 12    50 x 20       50   2.00000   Racer hit a wall at position ( 27, 14).
 13   100 x 100    2600   2.00000   Racer went out of bounds at position ( 105, 99).
 14    79 x 63      242   2.00000   Racer went out of bounds at position (-2, 26).
 15    26 x 1        25   0.32000   Racer reached goal at ( 25, 0) in 8 turns.
 16    17 x 1        19   2.00000   Racer went out of bounds at position (-2, 0).
 17    50 x 1        55   2.00000   Racer went out of bounds at position ( 53, 0).
 18    10 x 7        23   2.00000   Racer went out of bounds at position ( 10, 2).
 19    55 x 55       45   0.33333   Racer reached goal at ( 4, 49) in 15 turns.
 20    50 x 50      200   2.00000   Racer hit a wall at position ( 14, 7).
-------------------------------------------------------------------------------------
TOTAL SCORE:             26.45641

Vâng, tôi đã chạy nó, mặc dù tôi phải chạy nó từ thư mục chứa .classtệp vì một số lý do (thay vì thư mục chứa bộ điều khiển). Ping tôi (với một bình luận) nếu bạn quyết định thêm một testcase, vì vậy tôi có thể thêm nó vào điểm chuẩn. Điểm của bạn là khoảng 33 trên 10 lần chạy (xem bảng xếp hạng), nhưng điều này có thể thay đổi với mỗi bài kiểm tra mới được thêm vào điểm chuẩn.
Martin Ender

Ah có nó để chạy từ thư mục khác, quá. Đối với những người không quen thuộc với Java trên dòng lệnh:java -cp path/to/class/file VectorRacing
Martin Ender

À, đúng rồi, tôi đã tạo ra rất nhiều lớp học (chính xác là 13). Tôi đã luôn chạy tập lệnh của bạn từ thư mục lớp, vì vậy tôi không thực sự kiểm tra điều đó. Tôi có thể làm một trường hợp thử nghiệm, nhưng tôi nghĩ rằng tôi sẽ cố gắng tạo ra một tay đua không phải là ngẫu nhiên trước tiên.
bút danh

Chắc chắn rồi. Nếu bạn làm như vậy, xin vui lòng thêm nó như một câu trả lời riêng biệt. (Và thoải mái thêm một trường hợp thử nghiệm với mỗi người trong số họ.)
Martin Ender

Thật không may, tôi đã phải thêm một quy tắc khác. Bộ nhớ dường như hạn chế hơn thời gian trong thử thách này, vì vậy tôi đã phải đặt ra một mức độ khó để giới hạn mức tiêu thụ bộ nhớ. Bất kỳ hoạt động nào trong đó tay đua của bạn sử dụng hơn 1GB bộ nhớ sẽ bị hủy bỏ để có hiệu lực tương tự như vượt quá giới hạn thời gian. Đối với nhóm bản nhạc hiện tại, điểm của bạn không bị ảnh hưởng bởi thay đổi này (và có thể sẽ không bao giờ được).
Martin Ender
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.