Futoshiki C 递归求解器

发布于 2025-01-03 15:24:48 字数 4425 浏览 0 评论 0 原文

所以我有这个程序应该解决 C 中的 futoshiki 难题 wich 从具有以下格式的文本文件加载:

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 |... 所以我设法加载文件,检查它是否满足数学运算符条件,但我陷入了递归函数

我想知道的是:

我是否选择了正确的方式来存储矩阵或我' ve 应该将数字与逻辑运算符相除吗?

如何对矩阵执行递归扩展以及如何跟踪某个步骤中使用的数字(以防我必须回溯)?

例如。假设我到达 index[j][j] 其中 j (矩阵大小),从那里开始我必须递减 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

So i have this program that should solve a futoshiki puzzle in C
wich is loaded from a text file having this formatting :

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

where 5 is the size of the matrix, and the numbers adjcent to the operators <, >, ^, v must satisfy the condition imposed by them, from the file all the characters on rows are divided by spaces
eg 0 |...
So I've managed to load the file, to check if it satisfies the math operators conditions, but I'm stuck on the recursive function

What I'd like to know:

Did i choose the right way to store the matrix or I've should have divided the numbers from the logical operators ?

How could I perform an recursive expansion on the matrix and how could I track the used number in a certain step(in case I would have to backtrack)?

eg. let's say I arrive at index[j][j] where j<n (size of matrix) , starting from there I would have to decrement j ("touching") only numbers and check if the sub-matrix satisfies the conditions

Here's what I've managed to code so far.

where :

char **readmat(int *n); //reads the matrix from the file eliminating the spaces between chars

void print(char **mat,int n); //prints the stored matrix

int check(char **mat,int n); //checks if items of a matrix of size n satisfies the math operators

int expand (char **mat,int n,int i); //this should be the recursive functions that gets an element at a time and checks if there's any condition to be satisfied, if so, increments it

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

solution of the example : As you can see the logical conditions area clearly satisfied

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

尘世孤行 2025-01-10 15:24:48

存储矩阵的方式应该不会太重要。你可以随心所欲地存储它,只要你能方便地获取/设置每个点的数值,并评估操作人员是否满意。

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

//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 应该将给定点设置为给定值。

The way you store the matrix shouldn't matter too much. You can store it however you like, as long as you can easily get/set the numerical value of each spot, and evaluate whether the operators are satisfied.

Very broadly, you can solve problems of this type by using an algorithm like this:

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

This algorithm does a brute-force recursive search, trying every possible value for each spot, and backtracking if it enters an illegal state. In the super-worst case, it runs in exponential time, but in practice, the isValid() call at the beginning will short-circuit any obviously infeasible branches, so it should finish reasonably quickly for a 5x5 input.

Implementation of isValid, isComplete, getEmptySpot, and setValue will depend on how you defined gameData.

isValid should check to see that the game data isn't in an illegal state - in your case, it should check that all the greater-than comparisons are correct, and check that every number appears only once in each row and column. These checks should ignore spots whose value is 0, since they are just a placeholder meaning "not filled in yet".

isComplete should check to see that no spots have a "not filled in yet" placeholder. (isValid(gameData) && isComplete(gameData)) implies that gameData is solved.

getEmptySpot should find a spot that hasn't been filled in yet. If you're concerned about speed, it should find a spot with the least number values that can be legally entered. This will reduce the width of the search tree pretty considerably.

Finally, setValue should set the given spot to the given value.

御弟哥哥 2025-01-10 15:24:48

我会

  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

在<之后code>-- 启动规则,含义很明确(至少对我来说):
第 1 行、第 3 列的值必须大于第 2 行、第 3 列的值

。等等。

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

  1. 在矩阵中搜索是否存在涉及 2 必须大于的单元格的规则另一个细胞。如果是,您可以立即在整个矩阵的另一个单元格重复点 1 中插入 1
  2. ,这样您将得到一个新的、部分求解的矩阵作为起始点
  3. 与上面的 4 相同,规则为“小于” 。您可以在相关单元格中输入 5
  4. 现在搜索是否有一行(或一列)填充了 4 个数字。如果是这样,第五个数字就很明显了。

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

I would

  1. remove the matrix size. it is obvious by reading the matrix itself
  2. remove pipes and other chars, only leaving the spaces
  3. add operators after the matrix, in a special "encoded" format
  4. a single function could take the rules and try to solve the matrix

matrix example:

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

After the -- start the rules, the meaning is clear (to me at least):
value at row 1, column 3 must be greater than value at row 2, column 3.

etc.

About the solver, I would start as follows:

  1. search in the matrix if there's a rule involving a cell with a 2 that must be greater than another cell. If yes, you can immediately insert a 1 in the other cell
  2. repeat point 1 for the entire matrix, so that you'll get a new, partially solved matrix as a starting point
  3. Same thing as above with 4s with the rule "less than". You can put a 5 in the related cell
  4. Now search if there's a row (or column) with 4 numbers filled. If so, the 5th number is obvious.

When you have completed the preceding steps you have a partially (maybe fully if you're lucky) solved matrix, then you have to write a core function that tries every combination but considering the dynamic rules (those in the file) and the static rules (those that make the game).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文