막대 사슬을 가진 폴리 아미노산 형성


20

배경

(폐쇄 된) 막대 사슬을 고려하십시오. 각 막대의 길이는 정수입니다. 주어진 사슬로 몇 개의 별개의 홀이없는 폴리 아미노 를 형성 할 수 있습니까? 다시 말해, 주어진 체인으로 축이 정렬 된면을 가진 서로 다른 교차하지 않는 다각형을 몇 개나 만들 수 있습니까?

예를 봅시다. 길이 1과 2의 막대 8 개로 구성된 특정 체인을 생각해 보자 [1, 1, 2, 2, 1, 1, 2, 2]. 회전 및 변환까지는 가능한 8 개의 폴리 아미노가 있습니다 (우리는 다른 반사를 계산합니다) :

여기에 이미지 설명을 입력하십시오

이 첫 번째 막대는 진한 파란색이며 시계 반대 방향으로 다각형을 가로지 릅니다 .

회전 감각은 위 예의 결과에 영향을 미치지 않습니다. 그러나 [3, 1, 1, 1, 2, 1, 1]다음과 같은 3 가지 폴리 아미노를 생성하는 다른 사슬을 고려 하십시오.

여기에 이미지 설명을 입력하십시오

시계 방향 순회가 필요하기 때문에 마지막 polyomino를 반영 하지 않습니다 .

길이가 더 유연한 체인을 가졌다면 [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 실제로는 다른 폴리오 니노 사이에 총 9 개의 반사를 형성 할 수 있습니다.

여기에 이미지 설명을 입력하십시오

도전

체인에 대한 설명이 배열 또는 유사하게 제공되면 둘레를 시계 반대 방향으로 돌면서 막대 를 순서대로 사용하여 형성 할 수있는 별개의 폴리 아미노산 수 (회전 및 변환까지)를 결정하십시오 .

전체 프로그램을 작성하고 컴파일 할 수있는 명령을 포함하고 (해당되는 경우) 명령 행에서 코드를 실행하십시오. 귀하의 언어에 대한 무료 컴파일러 / 통역사에 대한 링크도 포함하십시오.

프로그램은 STDIN의 입력을 읽어야합니다. 첫 번째 줄에는 정수 M 이 포함됩니다 . 다음 M 라인은 테스트 케이스가 될 것이며, 각각은 공백으로 분리 된로드 길이 목록이 될 것입니다. 프로그램은 M 행을 STDOUT에 인쇄해야하며 , 각 행은 단일 정수로 구성됩니다.

단일 스레드 만 사용해야합니다.

프로그램은 언제든지 1GB 이상의 메모리를 사용해서는 안됩니다. (이것은 완전히 엄격한 제한은 아니지만 실행 파일의 메모리 사용량을 모니터링하고 지속적으로 1GB 이상을 사용하거나 그보다 크게 급등하는 프로세스를 종료합니다.)

과도한 양의 사전 계산을 방지하려면 코드가 20,000 바이트를 넘지 않아야하며 파일을 읽지 않아야합니다.

또한 선택한 특정 테스트 사례에 맞게 최적화해서는 안됩니다 (예 : 결과 하드 코딩). 귀하가 의심하는 경우 새로운 벤치 마크 세트를 생성 할 권리가 있습니다. 테스트 세트는 무작위이므로 그에 대한 프로그램의 성능은 임의의 입력에 대한 성능을 대표해야합니다. 당신이 할 수있는 유일한 가정은 막대 길이의 합이 균일하다는 것입니다.

채점

N = 10, 11, ..., 20 개의 체인에 대한 벤치 마크 세트제공했습니다 . 각 테스트 세트에는 길이가 1에서 4 사이 인 랜덤 체인 50 개가 포함됩니다.

1 차 점수는 프로그램이 전체 테스트 세트를 5 분 이내에 완료하는 데 가장 큰 N 입니다 (컴퓨터에서 Windows 8의 경우). 타이 브레이커는 해당 테스트 세트에서 프로그램이 실제로 소요 한 시간입니다.

누구든지 가장 큰 테스트 세트를 이길 경우 더 큰 테스트 세트를 계속 추가 할 것입니다.

테스트 사례

다음 테스트 사례를 사용하여 구현의 정확성을 확인할 수 있습니다.

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

멀티 스레딩이 허용됩니까? 스레드가 다른 프로세스에있는 경우 각 스레드가 1GB를 사용합니까? : P
feersum

@Sparr 경계선이 모퉁이에 닿을 때 가능합니다. 예를 들어 여기 81 번을 참조하십시오. 그 하나를 세지 마십시오.
Martin Ender

@feersum 간단하게하기 위해, 멀티 스레딩을 거부하고 도전을 편집 할 것입니다.
Martin Ender

1
@PeterKagey 잘못된 챌린지에이 코멘트를 게시 했습니까? 이것으로 가야했던 것 같습니다 .
마틴 엔더

답변:


5

C ++ 11

업데이트 : c거리가 원점에서 너무 멀면 첫 번째 줄이 조기에 추가됩니다 (변수의 전체 목적 rlen이었지만 첫 번째 버전에서 쓰는 것을 잊었습니다). 훨씬 적은 메모리를 사용하도록 변경했지만 시간이 걸립니다. 이제 5 분 안에 N = 20을 해결합니다.

#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

파이썬 3 ( 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>.


이것은 Martin과 질문을 논의 할 때 작성한 참조 구현입니다. 속도를 염두에두고 만들어지지 않았으며 매우 해킹되었지만 일을 시작하기위한 좋은 기준을 제공해야합니다.

겸손한 랩톱에서 N = 18은 약 2.5 분이 걸립니다.

연산

회전은 각 경계를 연속 F, A반 시계 방향 회전 및 C도형 경계의 각 격자 점에서 시계 방향 회전으로 변환하여 확인 합니다 . 이를 각도 문자열 이라고 합니다 . 각도 문자열이 순환 순열 인 경우 두 모양은 회전이 동일합니다. 항상 두 개의 앵글 스트링을 직접 비교하여이를 확인하는 대신 새로운 모양을 찾으면 저장하기 전에 표준 형식으로 변환합니다. 새로운 후보가 생겼을 때, 우리는 정식 형식으로 변환하고 이미 가지고 있는지 확인합니다 (따라서 전체 세트를 반복하지 않고 해싱을 이용하는 것).

각도 문자열은 또한 As의 수가 s의 수를 C4 만큼 초과 하는지 확인하여 모양이 시계 반대 방향으로 형성되는지 확인하는 데 사용됩니다 .

자체 교차점은 모양의 경계에 모든 격자 점을 저장하고 점을 두 번 방문했는지 확인하여 순진하게 검사됩니다.

핵심 알고리즘은 간단하여 첫 번째 막대를 오른쪽에 놓고 나머지 막대에 대한 모든 가능성을 시도합니다. 봉이 원점에서 너무 떨어진 지점에 도달하면 (즉, 남은 봉 길이의 합이 원점에서 점의 맨해튼 거리보다 작음) 해당 하위 트리 검색을 조기에 중지합니다.

업데이트 (최신 업데이트)

  • 6/12 : 정식 양식이 도입되었으며 몇 가지 미세 최적화가 추가되었습니다.
  • 5/12 : 알고리즘 설명의 오류 수정. A, B 순환 순열 iff B + B 방법의 하위 문자열을 사용하여 2 차 순환 순열 검사 알고리즘을 선형으로 만들었습니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.