2012-02-09 33 views
1

所以我有此程序,應該解決用C 至極一個futoshiki拼圖是由具有此格式的文本文件加載: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是矩陣的大小,以及數字adjcent給運營商<>^v必須滿足它們所規定的條件,從文件的行中的所有字符都用空格分開 如0 | ... 所以,我已經成功地加載該文件,以檢查它是否滿足數學運算符的條件,但我卡在recu上rsive功能

我想知道些什麼:

我有沒有選擇正確的方式來存儲矩陣或者我應該從邏輯運算符整除的數?

如何在矩陣上執行遞歸擴展,以及如何在特定步驟中跟蹤使用的數字(以防止我必須回溯)?

例如。比方說,我在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); //這應該是遞歸函數,它一次獲取一個元素並檢查是否有任何condi重刑得到滿足,如果是的話,增加它的例子

#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 
+2

我會保持號碼和運營商分開。 (我也會保持水平和垂直運算符分離。)畢竟,你只想在解決問題時更新數字,而不是操作員。 – 2012-02-09 13:54:32

+0

您確定您發佈的示例拼圖有解決方案嗎?用手工完成,我從下面第二排繼續得到兩個1。 – Kevin 2012-02-09 14:14:46

+0

@kevin當然是的,你肯定錯過了一些東西。 – 2012-02-09 14:30:10

回答

4

您存儲矩陣的方式不應該太重要。只要您可以輕鬆獲取/設置每個點的數值,並評估操作員是否滿意,您可以隨意存儲它。

非常廣泛,可以通過使用一種算法是這樣解決這一類型的問題:

//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應將給定點設置爲給定值。

+0

Now我必須出門,但是我會查看你張貼的日期代碼,並告訴你我是否覺得它有用,在第一眼看起來很好評論,謝謝你的努力。 – 2012-02-09 21:59:10

0

我會

  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列必須大於value值在第2行,第3列

關於解算器,我將開始如下:在基體

  1. 搜索如果存在涉及具有2的單元的規則,該單元必須大於另一單元。如果是的話,你可以立即在其他單元格中插入一個1作爲整個矩陣的重複點1,這樣你就可以得到一個新的部分解決的矩陣作爲起點
  2. 與上面相同的東西與4s規則「小於」。您可以在相關單元中放入一個5
  3. 現在搜索是否有一行(或列)填充了4個數字。如果是這樣,第五個數字是顯而易見的。

當你完成前面的步驟,你有一個部分(如果你幸運的話可能是完全的)求解矩陣,那麼你必須編寫一個核心函數來嘗試每個組合,但考慮動態規則文件)和靜態規則(製作遊戲的規則)。

+0

以及你的觀點是相當有效的,但請記住,這是我上週的一次考試,矩陣的格式是我發佈的一個格式,它使得它更加困難(這就是爲什麼他們還在字符之間添加空格。 – 2012-02-09 14:25:13