2012年5月24日 星期四

C# -> 解數獨

想要解數獨,通常是用「試誤法」,就是一格一格試
若不符合,則退一格再試,一直試到結束。

#include<stdio.h>
#include<stdlib.h>
int sodoku[10][10],post[10][10][2],cnt[82],blank[82][2] ; //sodoku是數獨表,post[i][j][0 or 1]記錄第(i,j)格屬於的宮的開頭坐標,cnt[i]是指第i個未知數目前被嘗試放入的數字,blank[i][0 or 1]分別代表待解之數的x和y坐標
int n,i,j,k,kx,ky,index,to,pass,fin ;
int pos(int x,int y) ;
int chk(int x,int y) ;
void prt(void) ;
int pos(int x,int y){
  int xy = 0 ;
  if(x <= n){
 xy = 10 ;
  }else if(n == 2){
    xy = 30 ;
  }else if(n == 3){
    if(x > n * 2)xy = 70 ;
    else xy = 40 ;
  }
 
  if(y <= n){
 xy += 1 ;
  }else if(n == 2){
    xy += 3 ;
  }else if(n == 3){
    if(y > n * 2)xy += 7 ;
    else xy += 4 ;
  }
 
  return xy ;
 
}
int chk(int x,int y){  //檢查目前的sodoku是否合法
  int ix,iy ;
 
  for(ix = 1 ; ix <= n * n ; ix++){
    if(ix != x && sodoku[ix][y] == sodoku[x][y])return 0 ;
  }
 
  for(iy = 1 ; iy <= n * n ; iy++){
    if(iy != y && sodoku[x][iy] == sodoku[x][y])return 0 ;
  }
 
  for(ix = post[x][y][0] ; ix < post[x][y][0] + n ; ix++){
    for(iy = post[x][y][1] ; iy < post[x][y][1] + n ; iy++){
   if(ix != x && iy != y && sodoku[ix][iy] == sodoku[x][y])return 0 ;
 }
  }
 
  return 1 ;
 
}
void prt(void){
  int ix,iy ;
  for(ix = 1 ; ix <= n * n ; ix++){
    for(iy = 1 ; iy <= n * n ; iy++){
   printf("%d ",sodoku[ix][iy]) ;
 }
    printf("\n") ;
  }
  printf("\n") ;
  //system("pause") ;
}
int main()
{
  while(scanf("%d",&n)!=EOF){
    to = index = 0 ;
    for(i = 1 ; i <= n * n ; i++)        //先讀資料(在此只考慮n<=3的情況)
      for(j = 1 ; j <= n * n ; j++){
        scanf("%d",&sodoku[i][j]) ;
        if(!sodoku[i][j]){       // 若讀到0,則表示這格是待解的數
          blank[to][0] = i ;
          blank[to][1] = j ;
          to += 1 ;
        }
     }
   
    for(i = 1 ; i <= n * n ; i++){//記錄第i列第j行是屬於哪一宮(以n=3來說,就是指九宮格)
      for(j = 1 ; j <= n * n ; j++){
        post[i][j][0] = pos(i,j) / 10 ;
        post[i][j][1] = pos(i,j) % 10 ;
      }
   }
 
    pass = 1 ;
    for(i = 1 ; i <= n * n ; i++){
      for(j = 1 ; j <= n * n ; j++){
        if(sodoku[i][j]){
          if(!chk(i,j)){
            pass = 0 ;
            break ;
          }
        }
     }
     if(!pass)break ;
   }
   
  if(!pass){
      printf("NO SOLUTION\n") ;
  }else{
   cnt[0] = 0 ;
   for(index = 0;;){   //開始一個一個跑
     if(cnt[index] < n * n){
       cnt[index] += 1 ;
       sodoku[blank[index][0]][blank[index][1]] = cnt[index] ;
       if(chk(blank[index][0],blank[index][1])){
         if(index < to - 1){
           index += 1 ;
           cnt[index] = 0 ;
         }else{
           prt() ;  //找到了,print出來
           fin = 1 ;
           break ;
         }
       }
     }else{
       if(!index){
         if(!fin)printf("NO SOLUTION\n") ;  //找不到,輸出NO SOLUTION
           break ;
       }else{
         sodoku[blank[index][0]][blank[index][1]] = 0 ;
         index -= 1 ;
       }
     }
   } //for(index;;)的右中括號
  } //if(!pass)的右中括號
 }  //while(scanf("%d",&n)!=EOF)的右中括號
  //system("pause") ;
  return 0 ;
}
全部都是未知數的話,找到一組字典順序在最前面的。

沒有留言:

張貼留言