Tic-tac-toe con solo cruces


32

Introducción

Todos conocen el juego de tres en raya, pero en este desafío, vamos a introducir un pequeño giro. Solo vamos a usar cruces . La primera persona que coloca tres cruces seguidas pierde. Un hecho interesante es que la cantidad máxima de cruces antes de que alguien pierda, es igual a 6 :

X X -
X - X
- X X

Eso significa que para un tablero de 3 x 3, la cantidad máxima es 6 . Entonces, para N = 3, necesitamos generar 6.

Otro ejemplo, para N = 4, o un tablero de 4 x 4:

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

Esta es una solución óptima, puede ver que la cantidad máxima de cruces es igual a 9 . Una solución óptima para una placa de 12 x 12 es:

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

Esto da como resultado 74 .

La tarea

La tarea es simple, dado un número entero mayor que 0, genera la cantidad máxima de cruces que se pueden colocar sin tres X adyacentes en una línea a lo largo de una fila, columna o diagonalmente.

Casos de prueba

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

Se puede encontrar más información en https://oeis.org/A181018 .

Reglas

  • Este es el , por lo que gana el envío con la menor cantidad de bytes.
  • Puede proporcionar una función o un programa.

77
Entonces, la pregunta se reduce al uso de las fórmulas en la página que
vinculaste

2
Publicación relacionada sobre matemáticas.
AdmBorkBork

77
@nicael Hasta donde puedo ver, el artículo de OEIS solo contiene límites inferiores.
Martin Ender

66
Sería genial ver esto como un desafío de código más rápido.
Lucas

44
No es una solución codegolf, pero he estado jugando con un solucionador "visual" en los últimos días. Puede acceder al jsfiddle aquí: jsfiddle.net/V92Gn/3899 Intenta encontrar soluciones a través de mutaciones aleatorias. No se detendrá si encuentra la respuesta "correcta", pero puede llegar a muchas de las soluciones correctas mucho más rápido que las respuestas a continuación.
styletron

Respuestas:


11

Pyth, 57 51 49 bytes

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

Al igual que la solución CJam de @ PeterTaylor, esta es la fuerza bruta, por lo que se ejecuta en tiempo O (n 2 2 n 2 ). El intérprete en línea no termina en un minuto para n = 4.

Pruébelo aquí para N <4.

Prueba la función de diagonales .

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

13

CJam ( 58 56 bytes)

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

Esto es increíblemente lento y usa mucha memoria, pero eso es para ti.

Disección

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

Θ(unaX)una1.83928675...Θ(unaX2)Θ(unaX4 4)


O(Xuna3X)

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

¿En qué se basa el enfoque eficiente?
lirtosiast

@ThomasKwa, oh, todavía es exponencial, pero creo que está justificado llamarlo eficiente porque me permitió extender la secuencia OEIS en 3 términos.
Peter Taylor

@ThomasKwa, para ser precisos, es O(n a^n)donde a ~= 5.518.
Peter Taylor

4

DO, 460 456 410 407 362 351 318 bytes

Esta es una muy mala respuesta. Es un enfoque de fuerza bruta increíblemente lento.Estoy tratando de jugar un poco más al golf combinando los forbucles.

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

Casos de prueba

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

Sin golf

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 */
}

Editar: declarar las variables int como parámetros no utilizados; elimina la coordenada y, solo usa index; mover variable a la lista de parámetros en lugar de global, corregir los parámetros innecesarios pasados ​​a s(); combinar para bucles, eliminar paréntesis innecesarios; reemplazar &&con *, ||con +; macro-ify la comprobación de 3 en una fila


Que tan lento es
Loovjo

¡@Loovjo lo intentó en mi PC con algunos pequeños cambios para hacerlo más rápido, 15 ms para n = 5, 12 segundos para n = 6 (entrada +1, tiempo * 800)!
edc65

@ edc65 Esa fue mi experiencia. Algo mayor que 5, el rendimiento fue dramáticamente más lento. No me molesté en probar entradas mayores a 6.
Cole Cameron

Comencé con 7 cuando publiqué mi comentario. Ya veremos
edc65

Puede exprimir algunos caracteres más con #define d(x,y)b[x]*b[x+y]*b[x+y+y] ; cambiando el inicio de sa s(i,m){for(m=n-2;y sustitución de todos los casos de n-2; y cambiando b[x]++a b[x++]++y luego reemplazando x==n*n-1con x==n*n, x+1con xy xcon x-1.
Peter Taylor

4

C 263 264 283 309

Editar Algunos bytes se guardaron gracias a Peter Taylor, menos de lo que esperaba. Luego, 2 bytes solían asignar más memoria, ahora puedo intentar un tamaño más grande, pero se está volviendo muy lento.

Nota Al agregar la explicación, descubrí que estoy desperdiciando bytes manteniendo la cuadrícula en la matriz R, para que pueda ver la solución encontrada ... ¡no se solicita para este desafío!
Lo eliminé en la versión de golf

Un programa de golf C que realmente puede encontrar la respuesta para n = 1..10 en un tiempo razonable.

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

Mi prueba:

7 -> 26 en 10 segundos
8 -> 36 en 18 segundos
9 -> 42 en 1162 segundos

Menos golf y tratando de explicar

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

Esto es esencialmente lo mismo que mi programa Java, pero primero en profundidad en lugar de primero en amplitud. Creo que deberías poder guardar al menos una docena de caracteres portando mi buildRowsmétodo; tal vez hasta 20 si for(scanf("%d",&n);(v=2*V[i++])<1<<n;v%8<6&&V[++j]=v+1)v&&V[++j]=v;es válido. (No tengo acceso a un compilador de C en este momento).
Peter Taylor

1
@PeterTaylor lo echaré un vistazo ... solo la palabra tribonacci me está asustando
edc65

Su codificación 999significa que querrá ignorar esa parte. Aunque tal vez realmente debería hacerlo no codificado, de modo que, en principio, pueda abordar entradas más grandes que 11 o 12.
Peter Taylor

@PeterTaylor funcionaría muy bien si tuviera un método .bitCount en C para contar bits. Pero en esa fase inicial, calcularé el conteo de bits en B, no solo las máscaras de bits en V
edc65 el

2

Rubí, 263 bytes

Esta también es una solución de fuerza bruta y enfrenta los mismos problemas que la respuesta C de Cole Cameron, pero es aún más lenta ya que es rubí y no C. Pero bueno, es más corta.

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}]

Casos de prueba

$ 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

Sin golf

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)

1

Haskell, 143 bytes

De alguna manera esto no se hace, pero me divertí, así que aquí va:

  • Debido a que la comprobación del patrón horizontal "ganador" devuelve no válido si se aplica en diferentes filas, la entrada de N <3 devuelve 0
  • Las "matrices" son enteros descomprimidos en bits, tan fácilmente enumerables
  • ((i! x) y) da el ith bit de x veces y, donde los índices negativos devuelven 0, por lo que el rango puede ser constante (sin verificación de límites) y menos paréntesis cuando está encadenado
  • Debido a que los límites no están marcados, verifica 81 * 4 = 324 patrones para cada máximo posible, lo que lleva a N = 3 a tomar mi laptop 9 segundos y a N = 5 tomar demasiado tiempo para que deje que termine
  • La lógica booleana en 1/0 se usa para T / F para ahorrar espacio, por ejemplo (*) es &&, (1-x) es (no x), etc.
  • Debido a que verifica enteros en lugar de matrices, (div p1 L) == (div p2 L) es necesario para asegurarse de que un patrón no se verifique en diferentes filas, donde L es la longitud de la fila y p1, p2 son posiciones
  • El valor de un máximo posible es su peso de Hamming

Aquí está el código:

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]]
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.