Tic-Tac-Toe mit nur Kreuzen

32

Einführung

Jeder kennt das Tic-Tac-Toe-Spiel, aber in dieser Herausforderung werden wir eine kleine Wendung einführen. Wir werden nur Kreuze verwenden . Die erste Person, die drei Kreuze hintereinander setzt, verliert. Eine interessante Tatsache ist, dass die maximale Anzahl an Kreuzen, bevor jemand verliert, gleich 6 ist :

X X -
X - X
- X X

Das bedeutet, dass für eine 3 x 3-Karte die maximale Anzahl 6 beträgt . Für N = 3 müssen wir also 6 ausgeben.

Ein weiteres Beispiel für N = 4 oder eine 4 x 4-Karte:

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

Dies ist eine optimale Lösung, Sie können sehen, dass die maximale Anzahl der Kreuze gleich 9 ist . Eine optimale Lösung für ein 12 x 12-Board ist:

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

Dies ergibt 74 .

Die Aufgabe

Die Aufgabe ist einfach: Geben Sie bei einer Ganzzahl größer als 0 die maximale Anzahl von Kreuzen aus, die platziert werden können, ohne dass drei X in einer Zeile, Spalte oder Diagonale benachbart sind.

Testfälle

N     Output
1     1
2     4
3     6
4     9
5     16
6     20
7     26
8     36
9     42

Weitere Informationen finden Sie unter https://oeis.org/A181018 .

Regeln

  • Das ist , also gewinnt die Einsendung mit der geringsten Anzahl von Bytes!
  • Sie können eine Funktion oder ein Programm bereitstellen.
Adnan
quelle
7
Die Frage beschränkt sich also auf die Verwendung der Formeln auf der Seite, die Sie verlinkt haben ...
nicael
2
In Verbindung stehender Beitrag vorbei auf Mathe.
AdmBorkBork
7
@nicael Soweit ich sehen kann, enthält der OEIS-Artikel nur Untergrenzen.
Martin Ender
6
Wäre cool, dies als schnellste Code-Herausforderung anzusehen.
Luke
4
Keine Codegolf-Lösung, aber ich habe in den letzten Tagen mit einem "visuellen" Solver gespielt. Sie können hier auf die jsfiddle zugreifen: jsfiddle.net/V92Gn/3899 Es wird versucht, Lösungen über zufällige Mutationen zu finden. Es wird nicht aufhören, wenn es die "richtige" Antwort findet, aber es kann viel schneller zu vielen der richtigen Lösungen kommen als diese Antworten unten.
Styletron

Antworten:

11

Pyth, 57 51 49 Bytes

L.T.e+*]Ykbbsef!s.AMs.:R3ssmyBdsm_BdCBcTQsD^U2^Q2

Wie bei der CJam-Lösung von @ PeterTaylor handelt es sich um Brute-Force-Lösung, die in O-Zeit (n 2 2 n 2 ) ausgeführt wird. Der Online-Dolmetscher ist für n = 4 nicht innerhalb einer Minute fertig.

Probieren Sie es hier für N <4.

Probieren Sie die Diagonalen-Funktion .

L.T.e+*]Ykbb         y(b): diagonals of b (with some trailing [])
s e                  sum of the last (with most ones) array such that
f                    filter lambda T:
 ! s .AM                none of the 3 element sublists are all ones               
   s .:R3               all 3 element sublists
   s s                  flatten
   myBd                 add the diagonals
   sm_B d               add the vertically flipped array and transpose
   CBcTQ                array shaped into Q by Q square, and its transpose
 sD ^U2 ^Q2             all binary arrays of length Q^2 sorted by sum
Lirtosiast
quelle
13

CJam ( 58 56 Bytes)

2q~:Xm*{7Yb#W=}:F,Xm*{ee{~0a@*\+}%zS*F},_Wf%:z&Mf*1fb:e>

Das ist unglaublich langsam und verbraucht viel Speicher, aber das ist für Sie.

Präparation

2q~:Xm*        e# Read input into X and find Cartesian product {0,1}^X
{7Yb#W=}:F,    e# Filter with a predicate F which rejects arrays with a 111
Xm*            e# Take the Cartesian product possible_rows^X to get possible grids
{              e# Filter out grids with an anti-diagonal 111 by...
  ee{~0a@*\+}% e#   prepending [0]*i to the ith row
  zS*F         e#   transposing, joining on a non-1, and applying F
},
_Wf%:z         e# Copy the filtered arrays and map a 90 degree rotation
&              e# Intersect. The rotation maps horizontal to vertical and
               e# anti-diagonal to diagonal, so this gets down to valid grids
Mf*            e# Flatten each grid
1fb            e# Count its 1s
:e>            e# Select the maximum

Θ(einX)ein1.83928675Θ(einX2)Θ(einX4)


O(Xein3X)

public class A181018 {
    public static void main(String[] args) {
        for (int i = 1; i < 14; i++) {
            System.out.format("%d:\t%d\n", i, calc(i));
        }
    }

    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;
    }
}
Peter Taylor
quelle
Was bringt der effiziente Ansatz?
Lirtosiast
@ThomasKwa, oh, es ist immer noch exponentiell, aber ich denke, es ist gerechtfertigt, es als effizient zu bezeichnen, weil es mir erlaubt ist, die OEIS-Sequenz um 3 Terme zu erweitern.
Peter Taylor
@ ThomasKwa, um genau zu sein, es ist O(n a^n)wo a ~= 5.518.
Peter Taylor
4

C, 460 456 410 407 362 351 318 Bytes

Das ist eine wirklich schlechte Antwort. Es ist ein unglaublich langsamer Brute-Force-Ansatz.Ich versuche, ein bisschen mehr Golf zu spielen, indem ich die forLoops kombiniere .

#define r return
#define d(x,y)b[x]*b[x+y]*b[x+2*(y)]
n,*b;s(i){for(;i<n*(n-2);++i)if(d(i%(n-2)+i/(n-2)*n,1)+d(i,n)+(i%n<n-2&&d(i,n+1)+d(i+2,n-1)))r 1;r 0;}t(x,c,l,f){if(s(0))r 0;b[x]++;if(x==n*n-1)r c+!s(0);l=t(x+1,c+1);b[x]--;f=t(x+1,c);r l>f?l:f;}main(c,v)char**v;{n=atol(v[1]);b=calloc(n*n,4);printf("%d",t(0,0));}

Testfälle

$ ./a.out 1
1$ ./a.out 2
4$ ./a.out 3
6$ ./a.out 4
9$ ./a.out 5
16$

Ungolfed

n,*b; /* board size, board */

s(i) /* Is the board solved? */
{
    for(;i<n*(n-2);++i) /* Iterate through the board */
            if(b[i%(n-2)+i/(n-2)*n]&&b[i%(n-2)+i/(n-2)*n+1]&&b[i%(n-2)+i/(n-2)*n+2] /* Check for horizontal tic-tac-toe */
                    || b[i] && b[i+n] && b[i+2*n] /* Check for vertical tic-tac-toe */
                    || (i%n<n-2
                            && (b[i] &&b [i+n+1] && b[i+2*n+2] /* Check for diagonal tic-tac-toe */
                                    || b[i+2*n] && b[i+n+1] && b[i+2]))) /* Check for reverse diagonal tic-tac-toe */
                    return 1;
    return 0;
}

t(x,c,l,f) /* Try a move at the given index */
{
    if(s(0)) /* If board is solved, this is not a viable path */
            return 0;
    b[x]++;
    if(x==n*n-1) /* If we've reached the last square, return the count */
            return c+!s(0);

    /* Try it with the cross */
    l=t(x+1,c+1);

    /* And try it without */
    b[x]--;
    f=t(x+1,c);

    /* Return the better result of the two */
    return l>f?l:f;
}

main(c,v)
char**v;
{
    n=atol(v[1]); /* Get the board size */
    b=calloc(n*n,4); /* Allocate a board */
    printf("%d",t(0,0)); /* Print the result */
}

Bearbeiten: Deklarieren Sie int-Variablen als nicht verwendete Parameter. y-Koordinate entfernen, nur Index verwenden; Verschieben Sie die Variable in die Parameterliste und nicht in die globale Liste. Korrigieren Sie unnötige Parameter, die an übergeben wurden s(). kombiniere für Schleifen, entferne unnötige Klammern; ersetzen &&mit *, ||mit +; Makro-ify die 3-in-eine-Reihe-Prüfung

Cole Cameron
quelle
Wie langsam ist es
Loovjo
@ Loovjo versuchte es auf meinem PC mit ein paar kleinen Änderungen, um es schneller zu machen, 15 ms für n = 5, 12 s für n = 6 (Eingabe +1, Zeit * 800)!
EDC65
@ edc65 Das war meine Erfahrung. Bei mehr als 5 war die Leistung dramatisch langsamer. Ich habe mich nicht darum gekümmert, Eingaben von mehr als 6 zu versuchen.
Cole Cameron
Ich habe mit 7 angefangen, als ich meinen Kommentar gepostet habe. Wir werden sehen
edc65
Sie können ein paar Zeichen mehr mit auspressen #define d(x,y)b[x]*b[x+y]*b[x+y+y]; durch den Beginn der Wechsel szu s(i,m){for(m=n-2;und ersetzt alle Instanzen n-2; und durch Ändern b[x]++zu b[x++]++und dann ersetzt x==n*n-1mit x==n*n, x+1mit x, und xmit x-1.
Peter Taylor
4

C 263 264 283 309

Bearbeiten Ein paar Bytes gespart dank @Peter Taylor - weniger als ich gehofft hatte. Dann 2 Bytes, um etwas mehr Speicher zuzuweisen, jetzt kann ich es mit einer größeren Größe versuchen, aber es wird sehr zeitaufwendig.

Hinweis Beim Hinzufügen der Erklärung habe ich festgestellt, dass ich Bytes verschwenden muss, um das Raster im R-Array zu halten - damit Sie die gefundene Lösung sehen können ... für diese Herausforderung ist sie nicht erforderlich !!
Ich habe es in der Golfversion entfernt

Ein Golf-C-Programm, das tatsächlich die Antwort für n = 1..10 in angemessener Zeit finden kann.

s,k,n,V[9999],B[9999],i,b;K(l,w,u,t,i){for(t=u&t|u*2&t*4|u/2&t/4,--l; i--;)V[i]&t||(b=B[i]+w,l?b+(n+2)/3*2*l>s&&K(l,b,V[i],u,k):b>s?s=b:0);}main(v){for(scanf("%d",&n);(v=V[i]*2)<1<<n;v%8<6?B[V[k]=v+1,k++]=b+1:0)V[k]=v,b=B[k++]=B[i++];K(n,0,0,0,k);printf("%d",s);}

Mein Test:

7 -> 26 in 10 Sek.
8 -> 36 in 18 Sek.
9 -> 42 in 1162 Sek

Weniger golfen und versuchen zu erklären

#include <stdio.h>

int n, // the grid size
    s, // the result
    k, // the number of valid rows 
    V[9999], // the list of valid rows (0..to k-1) as bitmasks
    B[9999], // the list of 'weight' for each valid rows (number of set bits)
    R[99],  // the grid as an array of indices pointing to bitmask in V
    b,i; // int globals set to 0, to avoid int declaration inside functions

// recursive function to fill the grid
int K(
  int l, // number of rows filled so far == index of row to add
  int w, // number of crosses so far
  int u, // bit mask of the preceding line (V[r[l-1]])
  int t, // bit mask of the preceding preceding line (V[r[l-2]])
  int i) // the loop variables, init to k at each call, will go down to 0
{
  // build a bit mask to check the next line 
  // with the limit of 3 crosses we need to check the 2 preceding rows
  t = u&t | u*2 & t*4 | u/2 & t/4; 
  for (; i--; )// loop on the k possibile values in V
  {
    R[l] = i; // store current row in R
    b = B[i] + w; // new number of crosses if this row is accepted
    if ((V[i] & t) == 0) // check if there are not 3 adjacent crosses
      // then check if the score that we can reach from this point
      // adding the missing rows can eventually be greater
      // than the current max score stored in s
      if (b + (n + 2) / 3 * 2 * (n - l - 1) > s)
        if (l > n-2) // if at last row
          s = b > s ? b : s; // update the max score
        else  // not the last row
          K(l + 1, b, V[i], u, k); // recursive call, try to add another row
  }
}

int main(int j)
{
  scanf("%d", &n);

  // find all valid rows - not having more than 2 adjacent crosses
  // put valid rows in array V
  // for each valid row found, store the cross number in array B
  // the number of valid rows will be in k
  for (; i<1 << n; V[k] = i++, k += !b) // i is global and start at 0
    for (b = B[k] = 0, j = i; j; j /= 2) 
      b = ~(j | -8) ? b : 1, B[k] += j & 1;
  K(0,0,0,0,k); // call recursive function to find the max score
  printf("%d\n", s);
}
edc65
quelle
Dies ist im Wesentlichen dasselbe wie in meinem Java-Programm, jedoch mit der Tiefe zuerst und nicht mit der Breite zuerst. Ich denke, Sie sollten in der Lage sein, mindestens ein Dutzend Zeichen zu speichern, indem Sie meine buildRowsMethode portieren. Vielleicht bis zu 20, wenn for(scanf("%d",&n);(v=2*V[i++])<1<<n;v%8<6&&V[++j]=v+1)v&&V[++j]=v;es gültig ist. (Ich habe momentan keinen Zugriff auf einen C-Compiler).
Peter Taylor
1
@PeterTaylor Ich werde es sehen ... nur das Wort Tribonacci macht mir Angst
edc65
Ihr fester Code 999bedeutet, dass Sie diesen Teil ignorieren möchten. Obwohl vielleicht sollte man es wirklich nicht hartcodieren, damit man prinzipiell größere Eingaben als 11 oder 12 angehen kann.
Peter Taylor
@ PeterTaylor es würde großartig funktionieren, wenn ich eine .bitCount-Methode in C hätte, um Bits zu zählen. Aber in dieser anfänglichen Phase berechne ich die Bitanzahl in B, nicht nur die Bitmasken in V
edc65,
2

Ruby, 263 Bytes

Dies ist auch eine Brute-Force-Lösung und hat dieselben Probleme wie die C-Antwort von Cole Cameron, ist aber noch langsamer, da dies rubinrot und nicht C ist. Aber hey, es ist kürzer.

c=->(b){b.transpose.all?{|a|/111/!~a*''}}
m=->(b,j=0){b[j/N][j%N]=1
x,*o=b.map.with_index,0
c[b]&&c[b.transpose]&&c[x.map{|a,i|o*(N-i)+a+o*i}]&&c[x.map{|a,i|o*i+a+o*(N-i)}]?(((j+1)...N*N).map{|i|m[b.map(&:dup),i]}.max||0)+1:0}
N=$*[0].to_i
p m[N.times.map{[0]*N}]

Testfälle

$ ruby A181018.rb 1
1
$ ruby A181018.rb 2
4
$ ruby A181018.rb 3
6
$ ruby A181018.rb 4
9
$ ruby A181018.rb 5
16

Ungolfed

def check_columns(board)
  board.transpose.all? do |column|
    !column.join('').match(/111/)
  end
end

def check_if_unsolved(board)
  check_columns(board) && # check columns
    check_columns(board.transpose) && # check rows
    check_columns(board.map.with_index.map { |row, i| [0] * (N - i) + row + [0] * i }) && # check decending diagonals
    check_columns(board.map.with_index.map { |row, i| [0] * i + row + [0] * (N - i) }) # check ascending diagonals
end

def maximum_crosses_to_place(board, index=0)
  board[index / N][index % N] = 1 # set cross at index
  if check_if_unsolved(board)
    crosses = ((index + 1)...(N*N)).map do |i|
      maximum_crosses_to_place(board.map(&:dup), i)
    end
    maximum_crosses = crosses.max || 0
    maximum_crosses + 1
  else
    0
  end
end

N = ARGV[0].to_i
matrix_of_zeros = N.times.map{ [0]*N }

puts maximum_crosses_to_place(matrix_of_zeros)
Siim Liiser
quelle
1

Haskell, 143 Bytes

In mancher Hinsicht wird das nicht gemacht, aber ich hatte Spaß, und so geht es weiter:

  • Da die Prüfung auf das horizontale "Gewinn" -Muster ungültig ist, wenn es auf verschiedene Zeilen angewendet wird, gibt die Eingabe von N <3 0 zurück
  • Die "Arrays" sind ganze Zahlen, die in Bits entpackt sind, also leicht aufzählbar
  • ((i! x) y) gibt das i-te Bit von x mal y an, wobei negative Indizes 0 zurückgeben, sodass der Bereich konstant sein kann (keine Begrenzungsüberprüfung) und weniger Klammern, wenn er verkettet ist
  • Da die Grenzen nicht überprüft werden, überprüft es 81 * 4 = 324 Muster für jedes mögliche Maximum, was dazu führt, dass N = 3 meinem Laptop 9 Sekunden und N = 5 zu lange dauert, damit ich es beenden kann
  • Boolesche Logik auf 1/0 wird für T / F verwendet, um Platz zu sparen, z. B. (*) ist &&, (1-x) ist (nicht x) usw.
  • Da statt Arrays Ganzzahlen geprüft werden, ist (div p1 L) == (div p2 L) erforderlich, um sicherzustellen, dass ein Muster nicht über verschiedene Zeilen hinweg geprüft wird, wobei L die Zeilenlänge und p1, p2 Positionen sind
  • Der Wert eines möglichen Maximums ist sein Hamming-Gewicht

Hier ist der Code:

r=[0..81]
(%)=div
s=sum
i!x|i<0=(*0)|0<1=(*mod(x%(2^i))2)
f l=maximum[s[i!x$1-s[s[1#2,l#(l+l),(l+1)#(l+l+2),(1-l)#(2-l-l)]|p<-r,let  a#b=p!x$(p+a)!x$(p+b)!x$s[1|p%l==(p+mod b l)%l]]|i<-r]|x<-[0..2^l^2]]
Michael Klein
quelle