Hình thành các đa giác với một chuỗi các thanh


20

Lý lịch

Hãy xem xét một chuỗi các thanh (đóng), mỗi thanh có chiều dài nguyên. Có bao nhiêu khác biệt lỗ miễn polyominoes bạn có thể hình thành với một chuỗi cho trước? Hay nói cách khác, bạn có thể tạo thành bao nhiêu đa giác không tự giao nhau với các cạnh thẳng hàng với một chuỗi nhất định?

Hãy xem xét một ví dụ. Hãy xem xét một chuỗi cụ thể bao gồm 8 thanh có chiều dài 1 và 2, mà chúng ta có thể đại diện là [1, 1, 2, 2, 1, 1, 2, 2]. Lên đến các phép quay và dịch, chỉ có 8 đa giác có thể (chúng tôi đếm các phản xạ khác nhau):

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

Thanh đầu tiên này có màu xanh đậm, và sau đó chúng ta đi qua đa giác theo nghĩa ngược chiều kim đồng hồ .

Ý nghĩa của xoay không ảnh hưởng đến kết quả trong ví dụ trên. Nhưng hãy xem xét một chuỗi khác [3, 1, 1, 1, 2, 1, 1], mang lại 3 đa giác sau:

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

Lưu ý rằng chúng tôi không bao gồm sự phản ánh của polyomino cuối cùng, bởi vì nó sẽ yêu cầu di chuyển theo chiều kim đồng hồ.

Nếu chúng ta có một chuỗi linh hoạt hơn có cùng độ dài [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], chúng ta thực sự có thể tạo thành cả hai phản xạ giữa một số polyonin khác, tổng cộng là 9:

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

Các thách thức

Đưa ra một mô tả về một chuỗi, như một mảng hoặc tương tự, xác định số lượng đa giác riêng biệt mà bạn có thể hình thành (lên đến các phép quay và dịch) bằng cách sử dụng các thanh theo thứ tự trong khi đi xung quanh chu vi theo nghĩa ngược chiều kim đồng hồ.

Vui lòng viết một chương trình đầy đủ và bao gồm các lệnh để biên dịch (nếu có) và chạy mã của bạn từ dòng lệnh. Vui lòng bao gồm một liên kết đến một trình biên dịch / trình thông dịch miễn phí cho ngôn ngữ của bạn.

Chương trình của bạn nên đọc đầu vào từ STDIN. Dòng đầu tiên sẽ chứa một số nguyên M . Các dòng M tiếp theo sẽ là các trường hợp thử nghiệm, mỗi trường hợp sẽ là một danh sách các chiều dài thanh được phân tách bằng dấu cách. Chương trình của bạn nên in M dòng thành STDOUT, mỗi dòng bao gồm một số nguyên duy nhất - số lượng đa giác riêng biệt có thể được hình thành.

Bạn chỉ phải sử dụng một chủ đề duy nhất.

Chương trình của bạn không được sử dụng nhiều hơn 1 GB bộ nhớ bất cứ lúc nào. (Đây không phải là giới hạn hoàn toàn nghiêm ngặt, nhưng tôi sẽ giám sát việc sử dụng bộ nhớ thực thi của bạn và tiêu diệt bất kỳ quy trình nào sử dụng liên tục hơn 1 GB hoặc tăng đột biến trên nó.)

Để ngăn chặn quá nhiều tính toán trước, mã của bạn không được dài hơn 20.000 byte và bạn không được đọc bất kỳ tệp nào.

Bạn cũng không được tối ưu hóa đối với các trường hợp thử nghiệm cụ thể đã chọn (ví dụ: bằng cách mã hóa kết quả của chúng). Nếu tôi nghi ngờ rằng bạn làm như vậy, tôi bảo lưu quyền tạo các bộ chuẩn mới. Các bộ kiểm tra là ngẫu nhiên, do đó hiệu suất của chương trình của bạn trên các bộ đó phải là đại diện cho hiệu suất của nó trên đầu vào tùy ý. Giả định duy nhất bạn được phép đưa ra là tổng chiều dài của thanh là chẵn.

Chấm điểm

Tôi đã cung cấp các bộ chuẩn cho các chuỗi N = 10, 11, ..., 20 que. Mỗi bộ kiểm tra chứa 50 chuỗi ngẫu nhiên có độ dài từ 1 đến 4.

Điểm chính của bạn là N lớn nhất mà chương trình của bạn hoàn thành toàn bộ bài kiểm tra trong vòng 5 phút (trên máy của tôi, trong Windows 8). Bộ ngắt kết nối sẽ là thời gian thực tế mà chương trình của bạn thực hiện trên bộ kiểm tra đó.

Nếu bất cứ ai đánh bại bộ thử nghiệm lớn nhất, tôi sẽ tiếp tục thêm những cái lớn hơn.

Các trường hợp thử nghiệm

Bạn có thể sử dụng các trường hợp kiểm tra sau để kiểm tra tính đúng đắn của việc triển khai.

Input                            Output

1 1                              0
1 1 1 1                          1
1 1 1 1 1 1                      1
1 1 1 1 1 1 1 1                  3
1 1 1 1 1 1 1 1 1 1              9
1 1 1 1 1 1 1 1 1 1 1 1          36
1 1 1 1 1 1 1 1 1 1 1 1 1 1      157
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1  758
1 1 2 2 1 1 2 2                  8
1 1 2 2 1 1 2 2 1 1              23
1 1 2 2 1 1 2 2 1 1 2 2          69
1 2 1 2 1 2 1 2                  3
1 2 1 2 1 2 1 2 1 2 1 2          37
1 2 3 2 1 2 3 2                  5
1 2 3 2 1 2 3 2 1 2 3 2          23
3 1 1 1 2 1 1                    3
1 2 3 4 5 6 7                    1
1 2 3 4 5 6 7 8                  3
1 2 3 4 5 6 7 8 9 10 11          5
2 1 5 3 3 2 3 3                  4
4 1 6 5 6 3 1 4                  2
3 5 3 5 1 4 1 1 3                5
1 4 3 2 2 5 5 4 6                4
4 1 3 2 1 2 3 3 1 4              18
1 1 1 1 1 2 3 3 2 1              24
3 1 4 1 2 2 1 1 2 4 1 2          107
2 4 2 4 2 2 3 4 2 4 2 3          114

Bạn tìm thấy một tập tin đầu vào với những ở đây .

Bảng xếp hạng

   User          Language       Max N      Time taken (MM:SS:mmm)

1. feersum       C++ 11         19         3:07:430

2. Sp3000        Python 3       18         2:30:181

"Không lỗ" dường như không cần thiết. một chuỗi liền kề không thể tạo ra các đa giác lỗ ở vị trí đầu tiên.
Sparr

Là đa luồng được phép? Và nếu các luồng trong các quy trình khác nhau, mỗi người có được sử dụng 1 GB không? : P
frageum

@Sparr Nó có thể khi chu vi chạm vào chính nó ở một góc. Ví dụ, xem số 81 ở đây. Điều đó không nên được tính.
Martin Ender

@feersum Để đơn giản, tôi sẽ nói không với đa luồng (và sẽ chỉnh sửa thử thách).
Martin Ender

1
@PeterKagey Bạn đã đăng bình luận này về thử thách sai? Có vẻ như nó nên đi trên cái này .
Martin Ender

Câu trả lời:


5

C ++ 11

Cập nhật: Đã thêm dòng đầu tiên cthoát ra sớm nếu khoảng cách quá xa gốc (đó là toàn bộ mục đích của biến rlen, nhưng tôi đã quên viết nó trong phiên bản đầu tiên). Tôi đã thay đổi nó để sử dụng ít bộ nhớ hơn, nhưng với chi phí thời gian. Bây giờ nó giải quyết N = 20 chỉ trong chưa đầy 5 phút cho tôi.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <map>
#include <ctime>

#define M std::map
#define MS 999
#define l (xM*2+1)

#define KITTENS(A,B)A##B
#define CATS(A,B)KITTENS(A,B)
#define LBL CATS(LBL,__LINE__)
#define unt unsigned
#define SU sizeof(unt)
#define SUB (SU*8)
#define newa (nb^SZ||fail("blob"),nb+++blob)

#define D

struct vec {int x, y;};


unt s[MS*2];
int xM, sl[MS];
vec X[MS];

struct a;
struct a  { M<unt,unt>v;};

#define SZ ((1<<29)/sizeof(a))
a*blob;
unt nb;


int fail(const char*msg)
{
    printf("failed:%s", msg);
    exit(1);
    return 1;
}

struct
{
    unt*m;
    bool operator()(int x, int y) { return m[(x+l*y)/SUB] >> (x+l*y)%SUB & 1; }
    void one(int x, int y) { m[(x+l*y)/SUB] |= 1U << (x+l*y)%SUB; }
    void zero(int x, int y) { m[(x+l*y)/SUB] &= ~(1U << (x+l*y)%SUB); }
} g;

unt c(a*A, vec x, unt rlen, unt sn) {
    if((unt)x.y+abs(x.x) > rlen) return 0;
    if(!rlen) {
        vec *cl=X, *cr=X, *ct=X;
        for(unt i=1; i<sn; i++) {
            #define BLAH(Z,A,B,o,O) \
                if(X[i].A o Z->A || (X[i].A == Z->A && X[i].B O Z->B)) \
                   Z = X+i

            BLAH(cl,x,y,<,>);
            BLAH(cr,x,y,>,<);
            BLAH(ct,y,x,>,>);
        }
        unt syms = 1;
        #define BLA(H,Z) {bool sy=1;for(unt o=0; o<sn; o++) sy &= (int)(1|-(H))*sl[o] == sl[(Z-X+o)%sn]; syms += sy;}
        BLA(~o&1,cl)
        BLA(1,ct)
        BLA(o&1,cr)

        #ifdef D
            //printf("D");for(int i=0;i<sn;i++)printf(" %u",sl[i]);printf("\n");
            if(syms==3) fail("symm");
        #endif

        return syms;
    }
    if(!(x.x|x.y|!sn)) return 0;
    X[sn] = x;

    unt k = 0;
    for(auto it: A->v) {
        int len = it.first;
        bool ve = sn&1;
        int dx = ve?0:len, dy = ve?len:0;

        #define PPCG(O)(x.x O (ve?0:z), x.y O (ve?z:0))
        #define MACR(O) { \
            vec v2 = {x.x O dx, x.y O dy}; \
            if(v2.y<0||(!v2.y&&v2.x<0)||abs(v2.x)>xM||v2.y>xM) \
                goto LBL; \
            for(int z=1; z<=len; z++) \
                if(g PPCG(O)) \
                    goto LBL; \
            for(int z=1; z<=len; z++) \
                g.one PPCG(O); \
            sl[sn] = O len; \
            k += c(blob+it.second, v2, rlen - len, sn+1); \
            for(int z=1; z<=len; z++) \
                g.zero PPCG(O); \
            } LBL: \

    MACR(+);
    MACR(-);
    }

    return k;
}

void stuff(a *n, unt j, unt r, unt len1)
{
    unt t=0;
    for(unt i=j; i<j+r; i++) {
        t += s[i];
        if((int)t > xM || (len1 && t>len1)) break;
        if(len1 && t < len1) continue;
        int r2 = r-(i-j)-1;
        if(r2) {
            unt x;
            if(n->v.count(t))
                x = n->v[t];
            else
                n->v[t] = x = newa - blob;
            stuff(blob+x, i+1, r2, 0);
        } else n->v[t] = -1;
    }
}

int main()
{
    time_t tim = time(0);
    blob = new a[SZ];
    int n;
    scanf("%u",&n);
    while(n--) {
        nb = 0;
        unt ns=0, tl=0;
        while(scanf("%u",s+ns)) {
            tl += s[ns];
            if(++ns==MS) return 1;
            if(getchar() < 11) break;
        }
        xM = ~-tl/2;
        g.m = (unt*)calloc((xM+1)*l/SU + 1,4);

        memcpy(s+ns, s, ns*SU);

        unt ans = 0;
        for(unt len1 = 1; (int)len1 <= xM; len1++) {
            a* a0 = newa;
            for(unt i=0; i<ns; i++)
                stuff(a0, i, ns, len1);
            ans += c(a0, {}, tl, 0);
            for(unt i=0; i<nb; i++)
                blob[i].v.clear();
        }
        printf("%d\n", ans/4);
        free(g.m);
    }

    tim = time(0) - tim;
    printf("time:%d",(int)tim);
    return 0;
}

Biên dịch với

g++ --std=c++11 -O3 feersum.cpp -o feersum.exe

doze #defines tho
Soham Chowdhury

Trong trường hợp không có câu trả lời nào khác ... ở đây, hãy có tiền thưởng!
Sp3000

3

Python 3 (với PyPy ) - N = 18

ANGLE_COMPLEMENTS = {"A": "C", "F": "F", "C": "A"}
MOVE_ENUMS = {"U": 0, "R": 1, "D": 2, "L": 3}
OPPOSITE_DIR = {"U": "D", "D": "U", "L": "R", "R": "L", "": ""}

def canonical(angle_str):
    return min(angle_str[i:] + angle_str[:i] for i in range(len(angle_str)))

def to_angles(moves):
    """
    Convert a string of UDLR to a string of angles where
      A -> anticlockwise turn
      C -> clockwise turn
      F -> forward
    """

    angles = []

    for i in range(1, len(moves)):
        if moves[i] == moves[i-1]:
            angles.append("F")
        elif (MOVE_ENUMS[moves[i]] - MOVE_ENUMS[moves[i-1]]) % 4 == 1:
            angles.append("C")
        else:
            angles.append("A")

    if moves[0] == moves[len(moves)-1]:
        angles.append("F")
    elif (MOVE_ENUMS[moves[0]] - MOVE_ENUMS[moves[len(moves)-1]]) % 4 == 1:
        angles.append("C")
    else:
        angles.append("A")

    return "".join(angles)

def solve(rods):
    FOUND_ANGLE_STRS = set()

    def _solve(rods, rod_sum, point=(0, 0), moves2=None, visited=None, last_dir=""):
        # Stop when point is too far from origin
        if abs(point[0]) + abs(point[1]) > rod_sum:
            return

        # No more rods, check if we have a valid solution
        if not rods:
            if point == (0, 0):
               angle_str = to_angles("".join(moves2))

               if angle_str.count("A") - angle_str.count("C") == 4:
                   FOUND_ANGLE_STRS.add(canonical(angle_str))

            return

        r = rods.pop(0)

        if not visited:
            visited = set()
            move_dirs = [((r, 0), "R")]
            moves2 = []

        else:
            move_dirs = [((r,0), "R"), ((0,r), "U"), ((-r,0), "L"), ((0,-r), "D")]

        opp_dir = OPPOSITE_DIR[last_dir]

        for move, direction in move_dirs:
            if direction == opp_dir: continue

            new_point = (move[0] + point[0], move[1] + point[1])
            added_visited = set()
            search = True

            for i in range(min(point[0],new_point[0]), max(point[0],new_point[0])+1):
                for j in range(min(point[1],new_point[1]), max(point[1],new_point[1])+1):
                    if (i, j) != point:
                        if (i, j) in visited:
                            search = False

                            for a in added_visited:
                                visited.remove(a)

                            added_visited = set()                            
                            break

                        else:
                            visited.add((i, j))
                            added_visited.add((i, j))

                if not search:
                    break

            if search:
                moves2.append(direction*r)
                _solve(rods, rod_sum-r, new_point, moves2, visited, direction)
                moves2.pop()

            for a in added_visited:
                visited.remove(a)

        rods.insert(0, r)
        return

    _solve(rods, sum(rods))
    return len(FOUND_ANGLE_STRS)

num_rods = int(input())

for i in range(num_rods):
    rods = [int(x) for x in input().split(" ")]
    print(solve(rods))

Chạy với ./pypy <filename> .


Đây là cách thực hiện tham khảo mà tôi đã viết khi thảo luận về câu hỏi với Martin. Nó không được thực hiện với tốc độ trong tâm trí và khá hack, nhưng nó sẽ cung cấp một cơ sở tốt để khởi động mọi thứ.

N = 18 mất khoảng 2,5 phút trên máy tính xách tay khiêm tốn của tôi.

Thuật toán

Các phép quay được kiểm tra bằng cách chuyển đổi từng hình thành một chuỗi về Fphía trước, Acho lần lượt ngược chiều kim đồng hồ và Cquay theo chiều kim đồng hồ tại mỗi điểm mạng trên đường biên của hình - Tôi gọi đây là chuỗi góc . Hai hình dạng giống hệt nhau nếu các chuỗi góc của chúng là hoán vị theo chu kỳ. Thay vì luôn luôn kiểm tra điều này bằng cách so sánh trực tiếp hai chuỗi góc, khi chúng tôi tìm thấy một hình dạng mới, chúng tôi chuyển đổi thành một hình thức chính tắc trước khi lưu trữ. Khi chúng tôi có một ứng cử viên mới, chúng tôi chuyển đổi sang dạng chính tắc và kiểm tra xem chúng tôi đã có điều này chưa (do đó khai thác băm, thay vì lặp qua toàn bộ).

Chuỗi góc cũng được sử dụng để kiểm tra xem hình dạng có được hình thành ngược chiều kim đồng hồ hay không, bằng cách đảm bảo rằng số As vượt quá số lượngC s bằng 4.

Tự giao nhau được kiểm tra một cách ngây thơ bằng cách lưu trữ mọi điểm mạng trên đường biên của hình và xem nếu một điểm được truy cập hai lần.

Thuật toán cốt lõi rất đơn giản, đặt thanh đầu tiên sang phải, sau đó thử tất cả các khả năng cho các thanh còn lại. Nếu các thanh đạt đến một điểm quá xa điểm gốc (nghĩa là tổng chiều dài thanh còn lại nhỏ hơn khoảng cách Manhattan của điểm so với điểm gốc), thì chúng ta sẽ sớm ngừng tìm kiếm cây con đó.

Cập nhật (mới nhất trước)

  • 6/12: Giới thiệu mẫu chính tắc, thêm một vài tối ưu vi mô
  • 5/12: Đã sửa lỗi trong giải thích thuật toán. Tạo tuyến tính thuật toán kiểm tra chu kỳ hoán vị bậc hai bằng cách sử dụng các hoán vị tuần hoàn A, B iff Một chuỗi con của phương pháp B + B (Tôi không biết tại sao tôi không làm điều này sớm hơn).
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.