최대한 빨리 교차하는 틱택 토


10

루크의 요청피터 테일러 또한 이 문제에.

소개

누구나 틱택 토 게임을 알고 있지만,이 도전에서는 약간의 비틀기를 소개 할 것입니다. 우리는 십자가 만을 사용할 것 입니다. 세 개의 십자가를 연속으로 배치 한 첫 번째 사람이집니다. 흥미로운 사실은 누군가가 잃기 전에 최대 십자가의 양이 6 과 같다는 것입니다 .

X X -
X - X
- X X

즉, 3 x 3 보드의 경우 최대 금액은 6 입니다. 따라서 N = 3의 경우 6을 출력해야합니다.

N = 4 또는 4 x 4 보드의 다른 예 :

X X - X
X X - X
- - - -
X X - X

이것은 최적의 솔루션입니다. 최대 교 차량이 9와 같다는 것을 알 수 있습니다 . 12 x 12 보드를위한 최적의 솔루션은 다음과 같습니다.

X - X - X - X X - X X -
X X - X X - - - X X - X
- X - X - X X - - - X X
X - - - X X - X X - X -
- X X - - - X - - - - X
X X - X X - X - X X - -
- - X X - X - X X - X X
X - - - - X - - - X X -
- X - X X - X X - - - X
X X - - - X X - X - X -
X - X X - - - X X - X X
- X X - X X - X - X - X

결과는 74 입니다.

작업

당신의 임무는 가능한 빨리 결과를 계산하는 것입니다. 테스트 케이스에서 코드를 실행하겠습니다 13. 이 작업은 5 번 수행 된 후 평균 실행 시간을 계산합니다. 이것이 최종 점수입니다. 낮을수록 좋습니다.

테스트 사례

N     Output
1       1
2       4
3       6
4       9
5       16
6       20
7       26
8       36
9       42
10      52
11      64
12      74
13      86
14      100
15      114

자세한 내용은 https://oeis.org/A181018참조하십시오 .

규칙

  • 이것은 이므로 가장 빠른 제출이 승리합니다!
  • 전체 프로그램을 제공해야합니다 .
  • 프로그램을 실행하는 방법도 알려주십시오. 모든 프로그래밍 언어와 작동 방식에 익숙하지 않으므로 약간의 도움이 필요합니다.
  • 물론 코드는 각 테스트 사례에 대한 올바른 결과 를 계산해야합니다 .

제출물 :

  • feersum (C ++ 11) : 28 초
  • 피터 테일러 (자바) : 14 분 31 초


내가 알 수 있는 한 이것은 두 번째 질문 의 속박이 아닙니까 , 당신은 단지 승리 조건을 변경 했습니까?
Blue

1
@muddyfish 도전 자체가 똑같아 보이지만,이 도전에 대한 접근 방식이 다른 도전과 매우 다르다는 것을 보장 할 수 있습니다 .
Adnan

3
@muddyfish 관련 메타 토론. "승리 조건 만 바꾸는 것"은 도전에 대한 실질적인 변화 일 수 있습니다. 가능한 모든 문제에 대해 코드 골프 , 가장 빠른 알고리즘가장 빠른 코드 를 게시하는 것은 의미가 없지만 두 가지 각도에서 문제를 탐색하면 사이트에 많은 가치를 부여 할 수있는 경우가 있습니다. 나는 이것이 그런 경우라고 생각합니다.
Martin Ender

1
멋진 도전! (+1)

답변:


5

C ++ 11, 28 대

또한 행을 기반으로 한 동적 프로그래밍 방식을 사용합니다. 나를 위해 논쟁 13을 실행하는 데 28 초가 걸렸습니다. 내가 가장 좋아하는 트릭은 next마스크와 행 3이 아닌 규칙을 만족하는 사전 식으로 다음 행 배열을 찾기 위해 약간의 비트 bashing을 사용 하는 함수입니다.

명령

  1. SEH 및 Posix 스레드가 있는 최신 MinGW-w64 설치
  2. 프로그램을 컴파일 g++ -std=c++11 -march=native -O3 <filename>.cpp -o <executable name>
  3. 로 실행 <executable name> <n>
#include <vector>
#include <stddef.h>
#include <iostream>
#include <string>

#ifdef _MSC_VER
#include <intrin.h>
#define popcount32 _mm_popcnt_u32
#else
#define popcount32 __builtin_popcount
#endif


using std::vector;

using row = uint32_t;
using xcount = uint8_t;

uint16_t rev16(uint16_t x) { // slow
    static const uint8_t revbyte[] {0,128,64,192,32,160,96,224,16,144,80,208,48,176,112,240,8,136,72,200,40,168,104,232,24,152,88,216,56,184,120,248,4,132,68,196,36,164,100,228,20,148,84,212,52,180,116,244,12,140,76,204,44,172,108,236,28,156,92,220,60,188,124,252,2,130,66,194,34,162,98,226,18,146,82,210,50,178,114,242,10,138,74,202,42,170,106,234,26,154,90,218,58,186,122,250,6,134,70,198,38,166,102,230,22,150,86,214,54,182,118,246,14,142,78,206,46,174,110,238,30,158,94,222,62,190,126,254,1,129,65,193,33,161,97,225,17,145,81,209,49,177,113,241,9,137,73,201,41,169,105,233,25,153,89,217,57,185,121,249,5,133,69,197,37,165,101,229,21,149,85,213,53,181,117,245,13,141,77,205,45,173,109,237,29,157,93,221,61,189,125,253,3,131,67,195,35,163,99,227,19,147,83,211,51,179,115,243,11,139,75,203,43,171,107,235,27,155,91,219,59,187,123,251,7,135,71,199,39,167,103,231,23,151,87,215,55,183,119,247,15,143,79,207,47,175,111,239,31,159,95,223,63,191,127,255};
    return uint16_t(revbyte[x >> 8]) | uint16_t(revbyte[x & 0xFF]) << 8;
}

// returns the next number after r that does not overlap the mask or have three 1's in a row
row next(row r, uint32_t m) {
    m |= r >> 1 & r >> 2;
    uint32_t x = (r | m) + 1;
    uint32_t carry = x & -x;
    return (r | carry) & -carry;
}

template<typename T, typename U> void maxequals(T& m, U v) {
    if (v > m)
        m = v;
}

struct tictac {
    const int n;
    vector<row> rows;
    size_t nonpal, nrows_c;
    vector<int> irow;
    vector<row> revrows;

    tictac(int n) : n(n) { }

    row reverse(row r) {
        return rev16(r) >> (16 - n);
    }

    vector<int> sols_1row() {
        vector<int> v(1 << n);
        for (uint32_t m = 0; !(m >> n); m++) {
            auto m2 = m;
            int n0 = 0;
            int score = 0;
            for (int i = n; i--; m2 >>= 1) {
                if (m2 & 1) {
                    n0 = 0;
                } else {
                    if (++n0 % 3)
                        score++;
                }
            }
            v[m] = score;
        }
        return v;
    }

    void gen_rows() {
        vector<row> pals;
        for (row r = 0; !(r >> n); r = next(r, 0)) {
            row rrev = reverse(r);
            if (r < rrev) {
                rows.push_back(r);
            } else if (r == rrev) {
                pals.push_back(r);
            }
        }
        nonpal = rows.size();
        for (row r : pals) {
            rows.push_back(r);
        }
        nrows_c = rows.size();
        for (int i = 0; i < nonpal; i++) {
            rows.push_back(reverse(rows[i]));
        }
        irow.resize(1 << n);
        for (int i = 0; i < rows.size(); i++) {
            irow[rows[i]] = i;
        }
        revrows.resize(1 << n);
        for (row r = 0; !(r >> n); r++) {
            revrows[r] = reverse(r);
        }
    }

    // find banned locations for 1's given 2 above rows
    uint32_t mask(row a, row b) {
        return ((a & b) | (a >> 1 & b) >> 1 | (a << 1 & b) << 1) /*& ((1 << n) - 1)*/;
    }

    int calc() {
        if (n < 3) {
            return n * n;
        }
        gen_rows();
        int tdim = n < 5 ? n : (n + 3) / 2;
        size_t nrows = rows.size();
        xcount* t = new xcount[2 * nrows * nrows_c]{};
#define tb(nr, i, j) t[nrows * (nrows_c * ((nr) & 1) + (i)) + (j)]

        // find optimal solutions given 2 rows for n x k grids where 3 <= k <= ceil(n/2) + 1

        {
            auto s1 = sols_1row();
            for (int i = 0; i < nrows_c; i++) {
                row a = rows[i];
                for (int j = 0; j < nrows; j++) {
                    row b = rows[j];
                    uint32_t m = mask(b, a) & ~(1 << n);
                    tb(3, i, j) = s1[m] + popcount32(a << 16 | b);
                }
            }
        }
        for (int r = 4; r <= tdim; r++) {
            for (int i = 0; i < nrows_c; i++) {
                row a = rows[i];
                for (int j = 0; j < nrows; j++) {
                    row b = rows[j];
                    bool rev = j >= nrows_c;
                    auto cj = rev ? j - nrows_c : j;
                    uint32_t m = mask(a, b);
                    for (row c = 0; !(c >> n); c = next(c, m)) {
                        row cc = rev ? revrows[c] : c;
                        int count = tb(r - 1, i, j) + popcount32(c);
                        maxequals(tb(r, cj, irow[cc]), count);
                    }
                }
            }
        }
        int ans = 0;
        if (tdim == n) { // small sizes
            for (int i = 0; i < nrows_c; i++) {
                for (int j = 0; j < nrows; j++) {
                    maxequals(ans, tb(n, i, j));
                }
            }
        } else {
            int tdim2 = n + 2 - tdim;
            // get final answer by joining two halves' solutions down the middle
            for (int i = 0; i < nrows_c; i++) {
                int apc = popcount32(rows[i]);
                for (int j = 0; j < nrows; j++) {
                    row b = rows[j];
                    int top = tb(tdim2, i, j);
                    int bottom = j < nrows_c ? tb(tdim, j, i) : tb(tdim, j - nrows_c, i < nonpal ? i + nrows_c : i);
                    maxequals(ans, top + bottom - apc - popcount32(b));
                }
            }
        }
        delete[] t;
        return ans;
    }
};


int main(int argc, char** argv) {
    int n;
    if (argc < 2 || (n = std::stoi(argv[1])) < 0 || n > 16) {
        return 1;
    }
    std::cout << tictac{ n }.calc() << '\n';
    return 0;
}

7

자바, 14 분 31 초

이것은 본질적으로 시퀀스를 확장하기 위해 OEIS에 게시 한 프로그램이므로 다른 사람들이 이길 수있는 좋은 참고 자료입니다. 보드 크기를 첫 번째 명령 줄 인수로 사용하도록 조정했습니다.

public class A181018 {
    public static void main(String[] args) {
        int n = Integer.parseInt(args[0]);
        System.out.println(calc(n));
    }

    private static int calc(int n) {
        if (n < 0) throw new IllegalArgumentException("n");
        if (n < 3) return n * n;

        // Dynamic programming approach: given two rows, we can enumerate the possible third row.
        // sc[i + rows.length * j] is the greatest score achievable with a board ending in rows[i], rows[j].
        int[] rows = buildRows(n);
        byte[] sc = new byte[rows.length * rows.length];
        for (int j = 0, k = 0; j < rows.length; j++) {
            int qsc = Integer.bitCount(rows[j]);
            for (int i = 0; i < rows.length; i++) sc[k++] = (byte)(qsc + Integer.bitCount(rows[i]));
        }

        int max = 0;
        for (int h = 2; h < n; h++) {
            byte[] nsc = new byte[rows.length * rows.length];
            for (int i = 0; i < rows.length; i++) {
                int p = rows[i];
                for (int j = 0; j < rows.length; j++) {
                    int q = rows[j];
                    // The rows which follow p,q cannot intersect with a certain mask.
                    int mask = (p & q) | ((p << 2) & (q << 1)) | ((p >> 2) & (q >> 1));
                    for (int k = 0; k < rows.length; k++) {
                        int r = rows[k];
                        if ((r & mask) != 0) continue;

                        int pqrsc = (sc[i + rows.length * j] & 0xff) + Integer.bitCount(r);
                        int off = j + rows.length * k;
                        if (pqrsc > nsc[off]) nsc[off] = (byte)pqrsc;
                        if (pqrsc > max) max = pqrsc;
                    }
                }
            }

            sc = nsc;
        }

        return max;
    }

    private static int[] buildRows(int n) {
        // Array length is a tribonacci number.
        int c = 1;
        for (int a = 0, b = 1, i = 0; i < n; i++) c = a + (a = b) + (b = c);

        int[] rows = new int[c];
        int i = 0, j = 1, val;
        while ((val = rows[i]) < (1 << (n - 1))) {
            if (val > 0) rows[j++] = val * 2;
            if ((val & 3) != 3) rows[j++] = val * 2 + 1;
            i++;
        }

        return rows;
    }
}

에 저장 A181018.java; 로 컴파일 javac A181018.java; 로 실행하십시오 java A181018 13. 내 컴퓨터에서이 입력을 실행하는 데 약 20 분이 걸립니다. 아마도 병렬화 할 가치가 있습니다.


얼마나 오랫동안 운영 했습니까? 여전히 OEIS에 용어를 추가하고 있습니다.
mbomb007

1
@ mbomb007, 아직 실행 중이 아닙니다. OEIS 날짜에서 알 수 있듯이 실행하는 데 며칠이 걸렸습니다 n=16. 나는 약 한 달이 걸릴 것이라고 추정 n=17했으므로 그것을 실행하려고하지 않았습니다. 메모리 사용량도 큰 문제가되었습니다. (PS 저는 현재 PPCG가 아닌 프로그래밍 문제에 4 개의 코어 중 2 개를 사용하고 있습니다 : azspcs.com/Contest/Tetrahedra/Standings )
Peter Taylor
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.