exact cover dancelink 精确覆盖(多解与一解)

QQ 1423173783   EMAIL 1423173783@qq.com      欢迎提出不同意见//如果数组a改为人工输入或从文件中读取。S[]可用程序计算 数组N[]这里没用是代码只要求输出那几行覆盖了所有列,如果代码要求哪一行覆盖了哪几列,它就能派上用场
//代码有个特点,用数组模拟了双向十字链表,时间复杂度不知道是变好了还是变坏了。
//双向十字链表,有列头没行头,这是可以改进的,我有预感,数独中是需要行头的。此代码用来求所有解,要遍历所有情况。
#include<iostream>
#include<cstdlib>
using namespace std;
int a[7][8]=    {0,0,0,0,0,0,0,0,
                       0,0,0,1,0,1,1,0,
                       0,1,0,0,1,0,0,1,
                       0,0,1,1,0,0,1,0,
                       0,1,0,0,1,0,0,0,
                       0,0,1,0,0,0,0,1,
                       0,1,1,1,1,1,1,1};
int U[100]={0},D[100]={0},R[100]={0},L[100]={0},S[8]={0,3,3,3,3,2,3,3},O[6]={0};
void remove(int c)
{
 L[R[c]]=L[c];
 R[L[c]]=R[c];
 for(int i=D[c];i!=c;i=D[i])
  for(int j=R[i];j!=i;j=R[j])
  {
   U[D[j]]=U[j];
   D[U[j]]=D[j];
   S[j%10]--;
  }
}
void resume(int c)
{
 for(int i=U[c];i!=c;i=U[i])
  for(int j=L[i];j!=i;j=L[j])
  {  
   U[D[j]]=j;
   D[U[j]]=j;
   S[j%10]++;
  }
    L[R[c]]=c;
 R[L[c]]=c;
}
void DFS(int k)
{
 if(!R[0]) 
 {
  for(int i=0;i<=5;i++)
  {cout<<O[i]<<"  ";  O[i]=0;}
     cout<<endl;
  return;
 }
 int count=100,c;
 for(int i=R[0];i;i=R[i])
  if(S[i]<count)
  {
   count=S[i];
   c=i;
   if(count==1) break;
  }
    remove(c);
  for(int i=D[c];i!=c;i=D[i])
 {
  O[k]=i/10;
  for(int j=R[i];j!=i;j=R[j])
   remove(j%10);
  DFS(k+1);
  for( int j=L[i];j!=i;j=L[j])  
   resume(j%10);
 }
 resume(c);
}
int main()
{
 
 char N[9]="1ABCDEFG";
 int row,col;
 for(col=0;col<=7;col++)
 {
  L[col]=col-1;
  R[col]=col+1;
 }
 L[0]=7;   R[7]=0;
 for(row=0;row<=6;row++)//在这个exact  cover  中是用不到的  但在sudoku中能用到
 {
  U[row]=row-1;
  D[row]=row+1;
 }
 U[0]=6;  D[6]=0;
 for(col=1;col<=7;col++)
 {
  int tmp[2]={0};
  row=1;
  while(a[row][col]==0)
  { row++; } ;
  tmp[0]=row; tmp[1]=col;
  D[col]=10*row+col;
  U[10*row+col]=col;
  for(int i=row+1;i<=6;i++)
      if(a[i][col]==0) continue;
      else
      {
           D[10*tmp[0]+tmp[1]]=10*i+col;
           U[10*i+col]=10*tmp[0]+tmp[1];
           tmp[0]=i; tmp[1]=col;
      }
  D[10*tmp[0]+tmp[1]]=col;
  U[col]=10*tmp[0]+tmp[1];
 }
 for(row=1;row<=6;row++)
 {
  int tmp[2]={0};
  col=1;
  while(a[row][col]==0)
  { col++; } ;
  tmp[0]=row; tmp[1]=col;
  int col8=col;
  for(int i=col+1;i<=7;i++)
      if(a[row][i]==0) continue;
      else
     {
         R[10*tmp[0]+tmp[1]]=10*row+i;
         L[10*row+i]=10*tmp[0]+tmp[1];
         tmp[0]=row; tmp[1]=i;
        }
   R[10*tmp[0]+tmp[1]]=10*row+col8;
  L[10*row+col8]=10*tmp[0]+tmp[1];
 }
 DFS(0);
 system("pause");
}

//此题是exact cover的一个特例的解法,如果数组a改为人工输入或从文件中读取。S[]可用程序计算 数组N[]这里没用是代码只要求输出那几行覆盖了所有列,如果代码要求哪一行覆盖了哪几列,它就能派上用场
//代码有个特点,用数组模拟了双向十字链表,时间复杂度不知道是变好了还是变坏了。
//双向十字链表,有列头没行头,这是可以改进的,我有预感,数独中是需要行头的。此代码用于求一个解就跳出不遍历所有情况。
#include<iostream>
#include<cstdlib>
using namespace std;
int a[7][8]=    {0,0,0,0,0,0,0,0,
                        0,0,0,1,0,1,1,0,
                        0,1,0,0,1,0,0,1,
                        0,0,1,1,0,0,1,0,
                        0,1,0,0,1,0,0,0,
                        0,0,1,0,0,0,0,1,
                        0,1,1,1,1,1,1,1};
int U[100]={0},D[100]={0},R[100]={0},L[100]={0},S[8]={0,3,3,3,3,2,3,3},O[6]={0};
void remove(int c)
{
 L[R[c]]=L[c];
 R[L[c]]=R[c];
 for(int i=D[c];i!=c;i=D[i])
  for(int j=R[i];j!=i;j=R[j])
  {
   U[D[j]]=U[j];
   D[U[j]]=D[j];
   S[j%10]--;
  }
}
void resume(int c)
{
 for(int i=U[c];i!=c;i=U[i])
  for(int j=L[i];j!=i;j=L[j])
  {  
   U[D[j]]=j;
   D[U[j]]=j;
   S[j%10]++;
  }
  L[R[c]]=c;
 R[L[c]]=c;
}
bool DFS(int k)
{
 if(!R[0])  return(true);
 int count=100,c;
 for(int i=R[0];i;i=R[i])
     if(S[i]<count)
     {
        count=S[i];
        c=i;
         if(count==1) break;
     }
  remove(c);
  for(int i=D[c];i!=c;i=D[i])
   {
        O[k]=i/10;
        for(int j=R[i];j!=i;j=R[j])
                 remove(j%10);
        if(DFS(k+1)) return (true);
        for( int j=L[i];j!=i;j=L[j])  
                 resume(j%10);
   }
 resume(c);
 return(false);
}
int main()
{
 char N[9]="1ABCDEFG";
 int row,col;
 for(col=0;col<=7;col++)
   {
         L[col]=col-1;
         R[col]=col+1;
   }
 L[0]=7;   R[7]=0;
 for(row=0;row<=6;row++)//在这个exact  cover  中是用不到的  但在sudoku中能用到
   {
        U[row]=row-1;
        D[row]=row+1;
   }
 U[0]=6;  D[6]=0;
 for(col=1;col<=7;col++)
   {
         int tmp[2]={0};
        row=1;
        while(a[row][col]==0)
        { row++; } ;
       tmp[0]=row; tmp[1]=col;
       D[col]=10*row+col;
       U[10*row+col]=col;
        for(int i=row+1;i<=6;i++)
              if(a[i][col]==0) continue;
             else
             {
                     D[10*tmp[0]+tmp[1]]=10*i+col;
                     U[10*i+col]=10*tmp[0]+tmp[1];
                     tmp[0]=i; tmp[1]=col;
            }
     D[10*tmp[0]+tmp[1]]=col;
     U[col]=10*tmp[0]+tmp[1];
 }
 for(row=1;row<=6;row++)
 {
      int tmp[2]={0};
      col=1;
      while(a[row][col]==0)
      { col++; } ;
       tmp[0]=row; tmp[1]=col;
       int col8=col;
       for(int i=col+1;i<=7;i++)
            if(a[row][i]==0) continue;
            else
               {
                     R[10*tmp[0]+tmp[1]]=10*row+i;
                     L[10*row+i]=10*tmp[0]+tmp[1];
                      tmp[0]=row; tmp[1]=i;
              }
       R[10*tmp[0]+tmp[1]]=10*row+col8;
        L[10*row+col8]=10*tmp[0]+tmp[1];
 }
 DFS(0);
 for(int i=0;i<=5;i++)
      cout<<O[i]<<"  ";
 cout<<endl;
 system("pause");
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值