การขึ้นรูป Polyominoes ด้วยสายโซ่ของแท่ง


20

พื้นหลัง

พิจารณาห่วงโซ่ของแท่ง (ปิด) ซึ่งแต่ละอันมีความยาวเต็ม คุณสามารถสร้างโพลีโอโน่แบบไม่มีรูได้หลายแบบด้วยโซ่ที่กำหนด หรืออีกนัยหนึ่งคุณสามารถสร้างรูปหลายเหลี่ยมที่ไม่ได้ตัดกันที่แตกต่างกันซึ่งมีด้านที่จัดเรียงตามแนวแกนได้ด้วยโซ่ที่กำหนด

ลองดูตัวอย่าง พิจารณาห่วงโซ่หนึ่งประกอบด้วย 8 แท่งของความยาว 1 และ 2 ซึ่งเราสามารถแทน[1, 1, 2, 2, 1, 1, 2, 2]ได้ มากถึงการหมุนและการแปลมีโพลีโม่เทาเพียง 8 ตัวที่เป็นไปได้ (เรานับการสะท้อนกลับที่แตกต่างกัน):

ป้อนคำอธิบายรูปภาพที่นี่

คันแรกนี้เป็นสีน้ำเงินเข้มจากนั้นเราสำรวจรูปหลายเหลี่ยมในแง่ทวนเข็มนาฬิกา

ความรู้สึกของการหมุนไม่ส่งผลต่อผลลัพธ์ในตัวอย่างข้างต้น แต่ลองพิจารณาอีกห่วงโซ่หนึ่ง[3, 1, 1, 1, 2, 1, 1]ซึ่งให้ผล 3 โปลิโม่ต่อไปนี้:

ป้อนคำอธิบายรูปภาพที่นี่

โปรดสังเกตว่าเราไม่ได้รวมการสะท้อนของโพลีโนมิโนสุดท้ายเพราะจะต้องมีการแวะผ่านตามเข็มนาฬิกา

หากเรามีโซ่ที่มีความยืดหยุ่นมากกว่าที่มีความยาวเท่ากัน[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]จริง ๆ แล้วเราจะสามารถสะท้อนการสะท้อนกลับของโพลีโอนิโนอื่น ๆ รวมเป็น 9:

ป้อนคำอธิบายรูปภาพที่นี่

ความท้าทาย

ให้คำอธิบายของโซ่เป็นอาร์เรย์หรือคล้ายกันกำหนดจำนวนของ polyominoes ที่แตกต่างกันคุณสามารถฟอร์ม (ขึ้นอยู่กับการหมุนและการแปล) โดยใช้แท่งตามลำดับในขณะที่ไปรอบปริมณฑลในแง่ทวนเข็มนาฬิกา

กรุณาเขียนโปรแกรมเต็มรูปแบบและรวมคำสั่งเพื่อรวบรวม (ถ้ามี) และเรียกใช้รหัสของคุณจากบรรทัดคำสั่ง โปรดใส่ลิงก์ไปยังคอมไพเลอร์ / ล่ามฟรีสำหรับภาษาของคุณ

โปรแกรมของคุณควรอ่านอินพุตจาก STDIN บรรทัดแรกจะมีจำนวนเต็มM บรรทัดMถัดไปจะเป็นกรณีทดสอบซึ่งแต่ละรายการจะเป็นรายการความยาวก้านที่คั่นด้วยช่องว่าง โปรแกรมของคุณควรพิมพ์บรรทัดMไปที่ STDOUT ซึ่งแต่ละรายการจะประกอบด้วยเลขจำนวนเต็มเดียวซึ่งเป็นจำนวนโพลีโม่แบบต่าง ๆ ที่สามารถเกิดขึ้นได้

คุณต้องใช้เธรดเดียวเท่านั้น

โปรแกรมของคุณต้องไม่ใช้หน่วยความจำมากกว่า 1 GB ในเวลาใดก็ได้ (นี่ไม่ใช่ข้อ จำกัด ที่เข้มงวดอย่างสมบูรณ์ แต่ฉันจะตรวจสอบการใช้งานหน่วยความจำของปฏิบัติการของคุณและฆ่ากระบวนการใด ๆ ที่ใช้มากกว่า 1 GB อย่างสม่ำเสมอหรือมีหนามแหลมด้านบน)

เพื่อป้องกันการคำนวณล่วงหน้ามากเกินไปรหัสของคุณจะต้องไม่เกิน 20,000 ไบต์และคุณต้องไม่อ่านไฟล์ใด ๆ

คุณต้องไม่ปรับให้เหมาะกับกรณีทดสอบเฉพาะที่เลือก (เช่นโดยการเข้ารหัสผลลัพธ์ของพวกเขา) หากฉันสงสัยว่าคุณทำฉันขอสงวนสิทธิ์ในการสร้างชุดมาตรฐานใหม่ ชุดทดสอบเป็นแบบสุ่มดังนั้นประสิทธิภาพของโปรแกรมของคุณสำหรับชุดนั้นควรเป็นตัวแทนสำหรับประสิทธิภาพของชุดข้อมูลนั้น ข้อสมมุติเดียวที่คุณอนุญาตให้ทำคือผลรวมของความยาวแท่งเป็นเท่ากัน

เกณฑ์การให้คะแนน

ฉันได้จัดทำชุดมาตรฐานสำหรับโซ่ของN = 10, 11, ... , 20 แท่ง ชุดทดสอบแต่ละชุดประกอบด้วย 50 โซ่สุ่มที่มีความยาวระหว่าง 1 และ 4 รวม

คะแนนหลักของคุณคือN ที่ใหญ่ที่สุดซึ่งโปรแกรมของคุณเสร็จสิ้นการทดสอบทั้งหมดภายใน 5 นาที (ในเครื่องของฉันภายใต้ Windows 8) tie breaker จะเป็นเวลาที่โปรแกรมของคุณทำการทดสอบ

หากใครชนะชุดทดสอบที่ใหญ่ที่สุดฉันจะเพิ่มชุดทดสอบที่ใหญ่กว่านี้ต่อไป

กรณีทดสอบ

คุณสามารถใช้กรณีทดสอบต่อไปนี้เพื่อตรวจสอบความถูกต้องของการนำไปใช้ของคุณ

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

คุณพบไฟล์อินพุตที่มีอยู่ตรงนี้

ลีดเดอร์บอร์ด

   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

"ไม่มีรู" ดูเหมือนไม่จำเป็น ห่วงโซ่ที่ต่อเนื่องกันอันเดียวไม่สามารถผลิตโพลีโอโมได้ในหลุมแรก
Sparr

อนุญาตให้ใช้มัลติเธรดได้หรือไม่ และถ้าเธรดอยู่ในกระบวนการที่แตกต่างกันแต่ละคนจะใช้ 1 GB หรือไม่? : P
feersum

@ Sparr สามารถเมื่อปริมณฑลสัมผัสกับตัวเองที่มุม ตัวอย่างเช่นดูหมายเลข 81 ที่นี่ ไม่ควรนับรวม
Martin Ender

@feersum เพื่อความง่ายฉันจะปฏิเสธไม่ทำมัลติเธรด (และจะแก้ไขความท้าทาย)
Martin Ender

1
@PeterKagey คุณโพสต์ความคิดเห็นนี้กับความท้าทายที่ผิดหรือเปล่า? ดูเหมือนว่ามันควรจะได้ไปอยู่กับคนนี้
Martin Ender

คำตอบ:


5

C ++ 11

อัปเดต: เพิ่มบรรทัดแรกcที่แบ่งออกก่อนถ้าระยะทางไกลเกินไปจากจุดเริ่มต้น (ซึ่งเป็นจุดประสงค์ทั้งหมดของตัวแปรrlenแต่ฉันลืมที่จะเขียนในเวอร์ชั่นแรก) ฉันเปลี่ยนมันเพื่อใช้หน่วยความจำน้อยลง แต่ใช้เวลา ตอนนี้มันแก้ปัญหา N = 20 ภายในเวลาไม่ถึง 5 นาทีสำหรับฉัน

#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;
}

รวบรวมกับ

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

doze #defines tho
Soham Chowdhury

ในกรณีที่ไม่มีคำตอบอื่น ๆ ... ที่นี่มีความโปรดปราน!
Sp3000

3

Python 3 (with 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))

./pypy <filename>ทำงานด้วย


นี่คือการดำเนินการอ้างอิงที่ฉันเขียนเมื่อพูดถึงคำถามกับมาร์ติน มันไม่ได้ทำด้วยความเร็วในใจและค่อนข้างแฮ็ค แต่ควรให้พื้นฐานที่ดีในการเตะสิ่งออก

N = 18 ใช้เวลาประมาณ 2.5 นาทีบนแล็ปท็อปขนาดเล็กของฉัน

ขั้นตอนวิธี

ผลัดจะถูกตรวจสอบโดยการแปลงแต่ละรูปร่างเป็นชุดของFสำหรับไปข้างหน้าAสำหรับการเปิดทวนเข็มนาฬิกาและCสำหรับการเปิดตามเข็มนาฬิกาในแต่ละจุดตาข่ายบนขอบของรูปร่าง - ผมเรียกนี้สตริงมุม สองรูปร่างเหมือนกันคือการหมุนถ้าสตริงมุมของพวกเขาเป็นวงจรเรียงสับเปลี่ยน แทนที่จะตรวจสอบสิ่งนี้เสมอโดยการเปรียบเทียบสตริงสองมุมโดยตรงเมื่อเราพบรูปทรงใหม่ที่เราแปลงเป็นรูปแบบมาตรฐานก่อนที่จะจัดเก็บ เมื่อเรามีผู้สมัครใหม่เราเปลี่ยนเป็นรูปแบบบัญญัติและตรวจสอบว่าเรามีสิ่งนี้อยู่แล้วหรือไม่

สตริงมุมยังถูกใช้เพื่อตรวจสอบว่ารูปร่างเกิดทวนเข็มนาฬิกาโดยตรวจสอบให้แน่ใจว่าจำนวนAs เกินจำนวนCs 4

มีการตรวจสอบการตัดกันด้วยตนเองโดยการเก็บทุกจุดขัดแตะบนขอบเขตของรูปร่างและดูว่ามีการเยี่ยมชมจุดสองครั้งหรือไม่

อัลกอริทึมหลักนั้นง่ายวางแท่งแรกไปทางขวาจากนั้นลองใช้โอกาสทั้งหมดสำหรับแท่งที่เหลือ หากแท่งถึงจุดที่อยู่ไกลจากจุดกำเนิด (เช่นผลรวมของความยาวแท่งที่เหลืออยู่นั้นน้อยกว่าระยะทางแมนฮัตตันจากจุดกำเนิด) เราจะหยุดการค้นหาทรีย่อยก่อนกำหนด

อัปเดต (ล่าสุดก่อน)

  • 6/12: เปิดตัวรูปแบบที่เป็นที่ยอมรับเพิ่มการเพิ่มประสิทธิภาพขนาดเล็กไม่กี่
  • 5/12: แก้ไขข้อผิดพลาดในคำอธิบายอัลกอริทึม ทำอัลกอริทึมการตรวจสอบการเปลี่ยนรูปแบบสมการกำลังสองเชิงเส้นโดยใช้ A, B การเปลี่ยนรูปแบบวงจร iff A substring ของวิธีการ B + B (ฉันไม่รู้ว่าทำไมฉันไม่ได้ทำสิ่งนี้มาก่อน)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.