Jiugongpin, encuentra una manera de resolver este problema~~Las ideas~el código está bien~~Se trata solo de su algoritmo de reducción, urgente~espera en línea. ~Gracias.
En nueve palacios de 3 × 3, hay ocho números del 1 al 8 y un espacio colocados aleatoriamente en la cuadrícula, como se muestra en la Figura 1-1. Ahora debemos reconocer este problema: ajustarlo a la forma que se muestra en el lado derecho de la Figura 1-1. La regla de ajuste es que solo un número adyacente a un espacio (arriba, abajo, izquierda o derecha) se puede convertir en un espacio a la vez. Intente resolver este problema mediante programación.
(Figura 1-1)
2. Análisis de preguntas:
Este es uno de los problemas clásicos de la inteligencia artificial. El problema es que en un tablero de ajedrez de 3×3 se colocan 8 casillas y el resto están vacías. Cada jugada sólo se puede intercambiar con espacios adyacentes. El programa genera automáticamente el estado inicial del problema y lo transforma en el arreglo objetivo a través de una serie de acciones de intercambio (Figura 1-2 a Figura 1-3 a continuación).
(Figura 1-2)(Figura 1-3)
En este problema, la disposición aleatoria generada por el programa se convierte en el objetivo * * *. Hay dos posibilidades. , y ambos no pueden ser verdaderos al mismo tiempo, es decir, disposición impar y disposición par. Una matriz dispuesta aleatoriamente se puede representar mediante una matriz unidimensional de izquierda a derecha y de arriba a abajo. Como se muestra en la figura anterior, 1-2 se puede expresar como {8, 7, 1, 5, 2, 6, 3, 4, 0} donde 0 representa un espacio.
En esta matriz, primero calculamos el resultado que se puede reordenar, la fórmula es:
∑ (F(X)) = y, donde F(X) p >
es un número cuyo frente es menor que este número. Hay soluciones cuando y es un número impar y un número par. (8) Determine si existe una solución para el problema numérico)
La matriz anterior puede resolver su resultado.
f(8)= 0;
f(7)=
f(1)=
f; (5)= 1;
f(2)= 1;
f(6)= 3;
f(3)= 2; p>
p>
f(4)= 3;
y = 0 0 0 1 1 3 2 3 = 10
Y = 10 es un número par , por lo que su reordenamiento es Los resultados se muestran en la Figura 1-3. Si el resultado de la suma es un reordenamiento impar, el resultado es el arreglo más a la derecha, como se muestra en la Figura 1-1.
3. Análisis de algoritmos
La solución es intercambiar las posiciones de los espacios (0) hasta alcanzar la posición objetivo. La representación gráfica es:
(Figura 3-1)
Si desea obtener lo mejor, debe utilizar la búsqueda en amplitud, ¡por lo que hay 9 Jiugong! Hay 362.880 arreglos y la cantidad de datos es muy grande. Para utilizar la búsqueda amplia, es necesario recordar la disposición de cada nodo. Si usa una matriz para grabar, ocupará mucha memoria, por lo que los datos se pueden comprimir adecuadamente. Guardar en formato DWORD. La forma comprimida es que cada número está representado por 3 bits, que es 3×9 = 27 bytes. Debido a que la representación binaria de 8 es 1000, no se puede representar con 3 bits. Un truco consiste en representar 8 como 000 y luego usar las cinco palabras adicionales para representar la posición de 8, de modo que pueda representarse mediante DWORD. Usar operaciones de cambio y OR para mover datos uno por uno es más rápido que la multiplicación. Se definen varios resultados para almacenar los resultados del recorrido y la ruta óptima una vez completada la búsqueda.
La estructura de esta clase es la siguiente:
Cuadrícula de clasificación
{
Pública:
Lista de ubicaciones de estructuras
{
Deward Plaza;
PlaceList * Izquierda
PlaceList * Derecha
};
estructura Scanbuf
{
Deward Square;
int ScanID
};
Lista de rutas de estructura
{
Ruta de caracteres sin firmar[9];
};
Privado:
PlaceList * m _ pPlaceList
Scanbuf * m _ pScanbuf
RECT m _ rResetButton
RECT m _ Botón Lauto
Público:
int m _ iPathsize
reloj _ t m _ iTime
UINT m _ iStepCount
Charm sin firmar m _ iTargetChess[ 9];
Carácter sin firmar m _ I ajedrez[9];
HWND m _ hClientWin
PathList * m _ pPathList p>
bool m_botolun;
Privado:
embedded bool AddTree(lugar DWORD, PlaceList *amp parent);
void free tree(lugar lista * & parent);
Inline void ArrayToDword(matriz de caracteres sin firmar, datos de amplificador DWORD);
inline void DwordToArray(datos DWORD, matriz de caracteres sin firmar *);
inline bool MoveChess); (matriz de caracteres sin firmar, forma int);
bool EstimateUncoil(matriz de caracteres sin firmar);
void GetPath(profundidad de la unidad);
Público:
void move chess(int way);
bool ComputeFeel();
void active shaw(HWND h view);
void dibujar cuadrícula (HDC HDC, cliente RECT rect);
void DrawChess(HDC hDC, cliente RECT rect);
void Reset();
botón anular (haga clic en pnt, vista HWND h);
Público:
cningrid();
~cningrid();
};
Utilice una plantilla vectorial para calcular una matriz aleatoria, utilice la función random_shuffle() para codificar los datos de la matriz y calcule el resultado objetivo.
Código:
void cn inegrid::Reset()
{
if(m_bAutoRun) devuelve;
Vector vs; p> p>
int I;
for(I = 1;ilt9;i)
vs.push_back(一);
vs. push _ back(0);
random_shuffle(vs.begin(), vs.end());
random_shuffle(vs.begin(), vs.end())
for(I = 0;ilt9;i)
{
m _ iChess[I]= vs[I];
}
if (! estimación sin envolver (m_iChess))
{
Matriz de caracteres sin firmar[9] = {1, 2, 3, 8, 0, 4, 7, 6, 5};
memcpy(m_iTargetChess, array, 9);
}
Otros
{
Matriz de caracteres sin firmar[9] = {1, 2, 3, 4, 5, 6, 7, 8, 0};
memcpy(m_iTargetChess, matriz, 9) ;
}
m _ iStepCount = 0;
}
Implementación de la función de compresión de datos:
cninegrid vacío en línea::ArrayToDword(unsigned char *array, DWORD amp data)
{
Noche de caracteres sin firmar = 0;
for( int I = 0 ; i lt9; i )
{
if (matriz[i] == 8)
{
noche = (sin firmar) char)I;
Romper
}
}
matriz[noche]= 0;
p>
datos = 0;
datos =(DWORD)((DWORD)matriz[0] lt; lt29 |(DWORD)matriz[1] lt; lt26 |
(DWORD)matriz[2] lt23 |(DWORD)matriz[3] lt20 |
(DWORD)matriz[4] lt17 |(DWORD)matriz[5] lt; lt14 |
(DWORD)matriz[6] lt; lt11 |(DWORD)matriz[7] lt8 |
(DWORD)matriz[8] lt5 |noche );
array[night]= 8;
}
La descompresión y la compresión son exactamente lo contrario.
Código de descompresión:
Cninegrid vacío en línea::DwordToArray(datos DWORD, matriz de caracteres sin firmar)
{
Chatem de caracteres sin firmar
for(int I = 0; i lt9; i)
{
chtem = (carácter sin signo)(datos gt gt(32-(I 1)* 3);0x 000000007 );
matriz[I]= chtem;
}
chtem = (carácter sin firmar)(datos amp0x 0000001F);
array[chtem]= 8;
}
Debido a que la cantidad de datos escalables es muy grande, se utiliza el tipo DWORD al guardar y cada paso de datos se registra en un árbol En el árbol binario ordenado, ordenado de pequeño a grande de izquierda a derecha, el tiempo de búsqueda es casi n veces más rápido que casi 10.000 veces cada vez. Varias funciones utilizadas en el bucle se declaran como funciones en línea, mientras que los datos insertados se buscan duplicados en el árbol para acelerar la velocidad general. Código de inserción de árbol binario:
AddTree(DWORD place, PlaceList * amp parent)
{
if (parent == NULL)
{
padre = nueva lista de lugares();
padre->; izquierda = padre->; derecha = vacío;
padre- gt; = Lugar;
Devuelve verdadero
}
if(parent - gt; Lugar == lugar
Devuelve falso
if(parent-gt; Colocar ubicación gt)
{
Devuelve AddTree(lugar, parent-gt; par);
} p >
Devuelve AddTree(place, parent- gt; left);
}
Si el resultado del cálculo es un número par o impar:
bool CNI negrid ::estimate dependicle(unsigned char*array)
{
int sun = 0
for(int I = 0; i lt8; i)
{
for(int j = 0; j lt9; j)
{
si (matriz[ j]!= 0)
{
if (matriz[j] == i 1)
Romper;
if( matriz[j] lt;i 1)
sol;
}
}
}
si (sun 2 = = 0)
Devuelve verdadero
Otros
Devuelve falso
}
Código que se mueve al espacio Es relativamente simple. Solo necesita calcular si se moverá fuera del cuadro y, cuando se mueva, calcule si ya es el resultado objetivo, que se utiliza para darle al usuario movimientos e indicaciones manualmente.
Código:
Inline bool cninegrid::move chess(unsigned char *array, int way)
{
int cero, cambiar
bool moveok = false
for(zero = 0; zero lt9; zero)
{
if(array[zero] == 0) p>
Rotura;
}
Punto pnt
pnt.x = cero 3;
pnt.y = int (cero/3);
Cambiar (vía)
{
Caso 0: //on
if(pnt . y 1 lt; 3)
{
Chang =(pnt . y 1)* 3 pto x;
matriz[cero]= matriz[ Chang] ;
matriz[Chang]= 0;
moveok = true
}
Romper;
Caso 1: //abajo
if(pnt . y-1 gt;-1)
{
Chang =(pnt . y-1) * 3 pnt . p>}
Descanso;
Caso 2: //izquierda
if(pnt . x 1 lt; 3)
{
Chang = pnt . y * 3 pnt x 1;
matriz[cero]= matriz[Chang];
matriz[Chang]= 0;
moveok = true
}
Break;
Caso 3: // Correcto
if (pnt .x-1 gt; -1)
{
Chang = pnt . [Chang];
array[Chang]= 0;
moveok = true
}
Romper;
}
if(moveok amp; amp! m_istepcount)
{
m_istepcount;
DWORD temp1, temp2
ArrayToDword(matriz, temp 1) ;
ArrayToDword(m_iTargetChess, temp 2);
if (temp1 == temp2)
{
MessageBox(NULL, " Eres tan inteligente, ¡Lo terminaste tan rápido!", "^_^", 0);
}
}
Volver a moveok
}
En la búsqueda amplia, el índice de matriz del nodo principal se registra en el nodo secundario. Por lo tanto, cuando se obtiene la disposición objetivo, se puede iniciar la búsqueda inversa desde el nodo secundario para obtener la búsqueda óptima. . camino. Utilice la variable m_iPathsize para registrar el número total de pasos.
El código de función específico es:
void cn inegrid::get path (profundidad de la unidad)
{
int now = 0, maxpos = 100; p>
p>
UINT parentid
if (m_pPathList!=null)
{
Eliminar[]m _ ppath list;
}
m_pPathList = nueva lista de rutas [max pos]
parentid = m_pScanbuf[profundidad]. ScanID
DwordToArray(m_pScanbuf[profundidad]). Lugar, m_pPathList[ahora]. ruta);
while(parentid!= -1)
{
si (ahora == maxpos)
{ p>
maxpos = 10;
lista de rutas * lista de tems = nueva lista de rutas[max pos];
memcpy(temlist, m_pPathList, sizeof(lista de rutas)*( max pos-10));
Eliminar[]m _ ppath list;
m _ pPathList = temlist
}
DwordToArray (m_pScanbuf[idpadre]). Lugar, m_pPathList[ahora]. ruta);
parentid = m_pScanbuf[parentid]. ScanID
}
m _ iPathsize = now
}
La función de demostración de disposición dinámica es la más simple. Para darle al formulario principal la oportunidad de actualizarse a tiempo, se inicia un hilo. Cuando sea necesario actualizar el formulario principal, simplemente use la función Slee (UINT) para pausar el hilo. Código:
Unsigned_ _ stdcall MoveChessThread(LPVOID PPAR am)
{
cningrid * p grid = (cningrid *)PP aram;
RECT Rectángulo;
p gird- gt; m_iStepCount = 0;
* GetClientRect(pg ird- gt; m_hClientWin amp; rect);
p >
for(int I = p gird- gt; m_iPathsize i gt0; i-)
{
memcpy(pg ird- gt; m_iChess, pg ird - gt; m_atleta[i].
ruta, 9);
p gird- gt; m _ istecount
invalidator(pg ird- gt; m _ hClientWin amp; rect, false
<); p>sleep(300);}
char msg[100];
sprintf(msg, "^_^! ¡Listo!\ r \ The el paso de cálculo toma d milisegundos", p grid- gt; m_iTime);
MessageBox(NULL, msg, "~_~", 0);
p gird-gt; m _ bAutoRun = false
Devuelve 0L;
}
Finalmente, se introduce el principio de la función de búsqueda. Primero, obtenga la matriz fuente y conviértala al tipo DWORD. Compare con el objetivo, si el mismo resultado es diferente, intercambie los datos y la posición espacial, únase al árbol binario y busque el siguiente resultado hasta que no quede ningún siguiente paso. De esta manera, antes de encontrar el resultado objetivo, la función:
bool cninegrid::ComputeFeel()
{
Unsigned char * array = m _ iChess p>
UINT I;
const int MAXSIZE = 362880
Matriz temporal de caracteres sin firmar[9];
Destino DWORD, fuente, ID de padre = 0, hijo = 1;
ArrayToDword(m_iTargetChess, objetivo);
ArrayToDword(matriz, fuente);
if(fuente==objetivo)
{
Devuelve falso
}
if (m_pScanbuf!=null)
{
eliminar[]m _ pScanbuf;
}
m _ pScanbuf = nuevo Scanbuf[MAXSIZE]
AddTree(fuente, m _ pplace lista;
m_pScanbuf[ 0 ]. Lugar = Fuente;
m_pScanbuf[ 0 ]. ScanID =-1;
reloj _ t Tim = reloj();
while(parentID lt;MAXSIZE ampchild ltMAXSIZE)
{
padre = m_pScanbuf[IDpadre]. Lugar;
for(I = 0; i lt4; i) // 0: arriba, 1: abajo, 2: izquierda, 3: derecha
{
DwordToArray(parent, temparray);
If (MoveChess(temparray, i)) //¿El movimiento fue exitoso?
{
ArrayToDword(temparray, fuente);
If (add tree (fountain, m_pplace list))//Agrega el número de búsquedas.
{
m_pScanbuf[niño].
lugar = fuente;
m_pScanbuf[niño]. ScanID = parentID
If (fountain == target) // ¿Encontraste el resultado?
{
m _ iTime = reloj()-Tim
obtener ruta(niño); //Calcular ruta
FreeTree (m _ pplace lista);
delete[]m _ pScanbuf
m _ pScanbuf = NULL
Devuelve verdadero
}
niño;
}
}
} //Para mí
parentid;
}
m _ iTime = reloj()-Tim;
FreeTree(m _ pplace lista
eliminar[]m _ pScanbuf;
m _ pScanbuf = NULL
Devuelve falso
}
Se presentan las funciones importantes, los siguientes son los resultados de ejecución y la operación; resultados del programa :