繁体   English   中英

Futoshiki C递归求解器

[英]Futoshiki C recursive solver

所以我有这个程序应该从具有这种格式的文本文件中加载Cwich中的futoshiki拼图:

5
0 | 0 | 0 | 0 | 0
- - - - v - - - - 
0 > 0 | 0 | 0 | 3
- - - - - - - - - 
0 | 0 < 2 | 0 | 0
- - - - v - - - - 
0 | 0 | 0 | 0 | 4
^ - v - - - - - - 
0 | 0 | 0 | 0 | 0

其中5是矩阵的大小,与运算符<>^v相邻的数字必须满足它们施加的条件,从文件中,行上的所有字符均由空格分隔,例如0 | ...因此,我设法加载了文件,以检查文件是否满足数学运算符的条件,但是我被卡在了递归函数上

我想知道的是:

我是否选择了正确的矩阵存储方式,还是应该将逻辑运算符中的数字相除?

如何在矩阵上进行递归扩展,如何在特定步骤中跟踪使用的数字(以防万一我必须回溯)?

例如。 假设我到达index[j][j] ,其中j<n (矩阵的大小),从那里开始,我只需要递减j (“触摸”)数字,然后检查子矩阵是否满足条件

到目前为止,这是我设法编写的代码。

在哪里:

char **readmat(int *n); //从文件中读取矩阵,消除字符之间的空格

void print(char **mat,int n); //打印存储的矩阵

int check(char **mat,int n); //检查大小为n的矩阵的项是否满足数学运算符

int expand (char **mat,int n,int i); //这应该是一次获取一个元素并检查是否满足任何条件的递归函数,如果满足,则将其递增

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


char **readmat(int *n);
void print(char **mat,int n);
int check(char **mat,int n); 
int expand (char **mat,int n,int i);

int main(int argc, char *argv[])
  {
  char **mat;
  int n, j;

  mat=readmat(&n);

  if(mat == NULL)
     return 1;

  if(check(mat,n)){
     print(mat,n);
  }
  else if(expand(mat,n,0)==1){
      print(mat,n);
  }
  else {
      printf("Nessuna soluzione trovata.\n");
  }

  for(j=0; j<=n;j++)
       free(mat[j]);
  free(mat);

  system("PAUSE");  
  return 0;
}

char **readmat(int *n){
     FILE *fp;
     char *line,nome[100];
     int i,j,k;
     char **mat;

     printf("Inserire il nome del file: ");
     scanf("%s",nome);
     fp=fopen(nome,"r");
     if(fp==NULL){
     printf("Errore apertura file");
     return NULL;
 }

 if(fgets(nome,100,fp)==NULL){
     printf("Formato file non valido\n");
     fclose(fp);
     return NULL;
 }
 if(sscanf(nome,"%d",n)!=1){
     printf("Errore nei parametri del file\n");
     fclose(fp);
     return NULL;    
 }

 (*n)=(((*n)*2)-1);


 mat=(char**)malloc((*n)*sizeof(char*));
 for(i=0;i<=(*n);i++)
    mat[i]=(char*)malloc((*n)*sizeof(char));

 line=(char*)malloc(2*(*n)*sizeof(char));

 i=0;

 while(i<=2*(*n) && fgets(line,2*(*n)+2,fp)!=NULL){
    j=0;
    k=0;
    while(j<=2*(*n)){
        if(line[j]!=' '){
           mat[i][k]=line[j];
           k++;
        }  
        j++;
    }
    i++;
 }   
 return mat;
 //print(mat, (*n));  
}

void print(char **mat,int n){
    int i=0,j=0;
    for (i=0; i<n; i++) {
     for (j=0; j<n; j++) {
        printf("%c", mat[i][j]);
     }
     printf("\n");
    }
}

int check(char **mat,int n) {

    int i,j;
    int k=1;

    for(i=0;i<n;i++){
        for(j=0;j<n;j++){
            if(mat[i][j]=='<'){
                if(mat[i][j-1] >= mat[i][j+1])
                    k=0;                               
            }
            else if(mat[i][j]=='>'){
                if(mat[i][j-1] <= mat[i][j+1])
                    k=0;
            }   
            else if(mat[i][j]=='^'){
                if(mat[i-1][j] >= mat[i+1][j])
                    k=0;    
            }
            else if(mat[i][j]=='v'){
                if(mat[i-1][j] <= mat[i+1][j])
                    k=0;                      
            }                            
        }
    }
    return k;                                
}
int expand (char **mat,int n,int i){

    int j=i/n;
    int k=i%n;
    int p;

    if(i>=n*n){

        return 1;
    }       
    else{
        if((mat[j][k]>47)&&(mat[j][k]<58)){
            if(mat[j][k]=='0'){     
                expand(mat,n,i+2);
            }   
            for (p=(mat[j][k]-48); p<(10-(mat[j][k]-48)); p++) {              
                mat[j][k]=48+p;                        
                if (check(mat,i)) {
              if (expand(mat, n, i+2)) {
                   return 1;
                    }
                }
            }
            i-=2;
            mat[j][k]='0';
        }
    }           
    return 0;
}

该示例的解决方案:如您所见,逻辑条件区域已明确满足

0 | 0 | 1 | 0 | 0
- - - - v - - - - 
1 > 0 | 0 | 0 | 3
- - - - - - - - - 
0 | 0 < 2 | 0 | 0
- - - - v - - - - 
0 | 1 | 0 | 0 | 4
^ - v - - - - - - 
1 | 0 | 0 | 0 | 0

您存储矩阵的方式应该没有太大关系。 您可以随意存储它,只要可以轻松获取/设置每个点的数值,并评估操作员是否满意即可。

广泛来说,您可以使用以下算法解决此类问题:

//returns true if this function solved the puzzle, false otherwise.
//gameData will be changed to the solved form of the puzzle if a solution exists, or remain unchanged if no solution exists.
//(so, whatever language you're using, ensure gameData is passed by reference here)
bool solve(gameData){
    if (!isValid(gameData)){return false;}  //oops, puzzle is unsolvable!
    if (isComplete(gameData)){return true;} //puzzle is already solved; no further modification needed.

    //choose a spot on the game board that hasn't been filled in yet.
    int x;
    int y;
    getEmptySpot(gameData, &x, &y);

    //iterate through all the possible values that could go into the empty spot.
    //you don't need anything fancy here to generate legal values for i;
    //if you accidentally supply an invalid value, then isValid()
    //will notice in the next solve() call.
    for (int i = 1; i <= 5; i++){
        //try putting i in the empty spot.
        setValue(gameData, x, y, i);
        if (solve(gameData)){ //putting i in the spot led to a solution!
            return true;
        }
    }
    //didn't find a solution :(
    //return gameData to its original state.
    setValue(gameData, x, y, 0);
    return false;
}

该算法进行强力递归搜索,为每个斑点尝试每个可能的值,如果进入非法状态则回溯。 在最坏的情况下,它以指数时间运行,但实际上,开始时的isValid()调用会使所有显然不可行的分支短路,因此对于5x5输入,它应该相当快地完成。

isValid,isComplete,getEmptySpot和setValue的实现将取决于您定义gameData的方式。

isValid应该检查游戏数据是否处于非法状态-在您的情况下,它应该检查所有大于比较是否正确,并检查每个数字在每一行和每一列中仅出现一次。 这些检查应忽略值为0的斑点,因为它们只是一个占位符,表示“尚未填写”。

isComplete应该检查是否没有斑点具有“尚未填充”占位符。 (isValid(gameData) && isComplete(gameData))表示已解决gameData。

getEmptySpot应该找到一个尚未填写的地点。 如果您担心速度,它应该找到一个可以合法输入的数值最小的点。 这将大大减少搜索树的宽度。

最后, setValue应该将给定位置设置为给定值。

我会

  1. 删除矩阵大小。 通过读取矩阵本身很明显
  2. 删除管道和其他字符,仅留空格
  3. 在矩阵之后以特殊的“编码”格式添加运算符
  4. 一个函数可以采用规则并尝试求解矩阵

矩阵示例:

0 0 0 0 0
0 0 0 0 3
0 0 2 0 0
0 0 0 0 4
0 0 0 0 0 
--
1,3>2,3
2,1>2,2
3,2<3,3
3,3>4,3
4,1<5,1
4,2>5,2

--开始规则之后,含义很明确(至少对我而言):第1行第3列的值必须大于第2行第3列的值。

等等

关于求解器,我将开始如下:

  1. 如果存在一个规则,其中涉及一个单元格的2必须大于另一个单元格,则在矩阵中搜索。 如果是,您可以立即在其他单元格中插入1
  2. 对整个矩阵重复点1,这样您将获得一个新的,部分求解的矩阵作为起点
  3. 与上述相同的规则为“小于”的4s。 您可以在相关单元格中放入5
  4. 现在搜索是否有一行(或一列)填充了4个数字。 如果是这样,第5个数字是显而易见的。

完成上述步骤后,您已经部分解决了矩阵(如果幸运的话可以完全解决),那么您必须编写一个核心函数来尝试每种组合,但要考虑动态规则(文件中的规则)和静态规则(制作游戏的人)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM