棒の鎖でポリオミノを形成する


20

バックグラウンド

それぞれが整数の長さを持つロッドの(閉じた)チェーンを考えてみましょう。特定のチェーンでいくつの穴のないポリオミノを形成できますか?または、言い換えれば、特定のチェーンを使用して、軸に沿った辺を持つ、いくつの異なる非自己交差ポリゴンを形成できますか?

例を見てみましょう。長さ1と2の8本の棒で構成される特定のチェーンを考えてみましょう[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になります。

ここに画像の説明を入力してください

チャレンジ

チェーンの説明を配列または類似のものとして、ロッドを順番に使用して反時計回りに周回しながら形成できる個別のポリオミノの数(回転と平行移動まで)を決定します。

完全なプログラムを作成し、コマンドラインからコードをコンパイルして(該当する場合)実行するコマンドを含めてください。ご使用の言語の無料のコンパイラ/通訳へのリンクも含めてください。

プログラムは、STDINからの入力を読み取る必要があります。最初の行には整数Mが含まれます。次のM行はテストケースで、各行はスペースで区切られたロッドの長さのリストです。プログラムは、M行をSTDOUTに出力する必要があります。各行は、単一の整数(形成可能な個別のポリオミノの数)で構成されます。

単一のスレッドのみを使用する必要があります。

プログラムは、常に1 GBを超えるメモリを使用しないでください。(これは完全に厳密な制限ではありませんが、実行可能ファイルのメモリ使用量を監視し、1 GBを常に使用するプロセス、またはそれを大幅に超えるプロセスを強制終了します。)

過剰な事前計算を防ぐために、コードは20,000バイトを超えてはならず、ファイルを読み取ってはなりません。

また、選択した特定のテストケースに向けて最適化しないでください(結果をハードコーディングするなど)。疑わしい場合は、新しいベンチマークセットを生成する権利を留保します。テストセットはランダムなので、それらのプログラムのパフォーマンスは、任意の入力でのパフォーマンスを代表するものでなければなりません。許可される唯一の仮定は、ロッドの長さの合計が偶数であるということです。

得点

N = 10、11、...、20ロッドのチェーンのベンチマークセット提供しました。各テストセットには、長さが1〜4のランダムチェーンが50個含まれています。

プライマリスコアは、プログラムが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

「ホールフリー」は不要なようです。そもそも、1つの連続したチェーンでは穴の開いたポリオミノを生成できません。
スパー14

マルチスレッドは許可されていますか?また、スレッドが異なるプロセスにある場合、各スレッドは1 GBを使用しますか?:P
feersum 14

@Sparr境界線が角で自分自身に触れると、できます。たとえば、ここでNo. 81を参照してください。それは数えられるべきではありません。
マーティンエンダー14

@feersum簡単にするために、マルチスレッド化にノーと言います(そしてチャレンジを編集します)。
マーティンエンダー14

1
@PeterKagey間違ったチャレンジについてこのコメントを投稿しましたか?それのようなルックスは上行っている必要があり、この1
マーティンエンダー

回答:


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

居眠り#defineのカントー
ソーハムChowdhuryの

他に答えがなければ...ここで、賞金をもらおう!
Sp3000 14

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図形の境界上の各格子点での時計回りの一連の変換に変換することでチェックされます。これを角度ストリングと呼びます。2つの形状は、角度ストリングが循環順列である場合、回転的に同一です。2つの角度文字列を直接比較することで常にこれをチェックするのではなく、新しい形状を見つけると、保存する前に正規の形式に変換します。新しい候補ができたら、標準形式に変換して、すでにあるかどうかを確認します(したがって、セット全体を反復するのではなく、ハッシュを利用します)。

角度文字列は、Asの数がCs の数を4 超えていることを確認することにより、形状が反時計回りに形成されていることを確認するためにも使用されます。

自己交差は、形状の境界上のすべての格子点を保存し、点が2回訪問されるかどうかを確認することにより、単純にチェックされます。

コアアルゴリズムは単純で、最初のロッドを右側に配置してから、残りのロッドのすべての可能性を試します。ロッドが原点から遠すぎるポイントに到達した場合(つまり、残りのロッドの長さの合計が原点からのポイントのマンハッタン距離よりも短い場合)、そのサブツリーの検索を途中で停止します。

更新(最新から)

  • 6/12:正規形が導入され、いくつかのマイクロ最適化が追加されました
  • 5/12:アルゴリズムの説明の誤りを修正。B + Bメソッドの部分文字列の場合、A、B巡回置換を使用して、二次巡回置換チェックアルゴリズムを線形にしました(以前にこれを行わなかった理由がわかりません)。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.