2 条题解

  • 0
    @ 2025-10-8 16:55:40

    B32 Dancing Links 舞蹈链 数独

    B32 Dancing Links 舞蹈链 数独

    9*9的dancing links代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int N=3245; //点729*4+列头324
    int n,m,cnt; //矩阵的长,宽,点的编号
    int u[N],d[N],l[N],r[N]; //每个点的上下左右
    int row[N],col[N]; //每个点所在行,列
    int h,t;  //每行的头,尾指针
    int s[N]; //每列的节点数
    int ans[N]; //选了那些行
    int a[10][10];//数独的数
    
    void init(){ //初始化第0行的列表头
      for(int y=0; y<=m; y++){
        u[y]=d[y]=y;
        l[y]=y-1; r[y]=y+1;
      }
      l[0]=m; r[m]=0; cnt=m+1; //下一个点的编号
    }
    void link(int x,int y){ //在x行y列插入点 
      row[cnt]=x; col[cnt]=y; s[y]++;
      u[cnt]=u[y]; //y...u[y]←→cnt←→y
      d[u[y]]=cnt;
      d[cnt]=y;
      u[y]=cnt;
      l[cnt]=t; //h...t←→cnt←→h
      r[t]=cnt;
      r[cnt]=h;
      l[h]=cnt;
      t=cnt++; //t指向cnt, 然后cnt+1
    }
    void remove(int y){ //删除y列与关联行
      r[l[y]]=r[y], l[r[y]]=l[y]; 
      for(int i=d[y]; i!=y; i=d[i])   //向下
        for(int j=r[i]; j!=i; j=r[j]) //向右
          u[d[j]]=u[j], d[u[j]]=d[j], s[col[j]]--;
    }
    void resume(int y){ //恢复y列与关联行
      r[l[y]]=y, l[r[y]]=y;  
      for(int i=u[y]; i!=y; i=u[i])   //向上
        for(int j=l[i]; j!=i; j=l[j]) //向左
          u[d[j]]=j, d[u[j]]=j, s[col[j]]++;
    }
    bool dance(int dep){
      if(r[0]==0){
        for(int i=0,x,y,v;i<dep;i++){
          x=(ans[i]-1)/9/9; //链表行→数独
          y=(ans[i]-1)/9%9;
          v=(ans[i])%9;
          a[x][y]=v?v:9; 
        }
        for(int i=0;i<=8;i++){
          for(int j=0;j<=8;j++)printf("%d ",a[i][j]);
          puts("");
    • 0
      @ 2025-10-8 16:55:10

      B32 Dancing Links 舞蹈链 数独

      9*9的dancing links代码:

      #include<iostream>
      #include<cstdio>
      #include<cstring>
      #include<algorithm>
      using namespace std;

      const int N=3245; //点729*4+列头324 int n,m,cnt; //矩阵的长,宽,点的编号 int u[N],d[N],l[N],r[N]; //每个点的上下左右 int row[N],col[N]; //每个点所在行,列 int h,t; //每行的头,尾指针 int s[N]; //每列的节点数 int ans[N]; //选了那些行 int a[10][10];//数独的数

      void init(){ //初始化第0行的列表头 for(int y=0; y<=m; y++){ u[y]=d[y]=y; l[y]=y-1; r[y]=y+1; } l[0]=m; r[m]=0; cnt=m+1; //下一个点的编号 } void link(int x,int y){ //在x行y列插入点 row[cnt]=x; col[cnt]=y; s[y]++; u[cnt]=u[y]; //y...u[y]←→cnt←→y d[u[y]]=cnt; d[cnt]=y; u[y]=cnt; l[cnt]=t; //h...t←→cnt←→h r[t]=cnt; r[cnt]=h; l[h]=cnt; t=cnt++; //t指向cnt, 然后cnt+1 } void remove(int y){ //删除y列与关联行 r[l[y]]=r[y], l[r[y]]=l[y]; for(int i=d[y]; i!=y; i=d[i]) //向下 for(int j=r[i]; j!=i; j=r[j]) //向右 u[d[j]]=u[j], d[u[j]]=d[j], s[col[j]]--; } void resume(int y){ //恢复y列与关联行 r[l[y]]=y, l[r[y]]=y;
      for(int i=u[y]; i!=y; i=u[i]) //向上 for(int j=l[i]; j!=i; j=l[j]) //向左 u[d[j]]=j, d[u[j]]=j, s[col[j]]++; } bool dance(int dep){ if(r[0]0){ for(int i=0,x,y,v;i<dep;i++){ x=(ans[i]-1)/9/9; //链表行→数独 y=(ans[i]-1)/9%9; v=(ans[i])%9; a[x][y]=v?v:9; } for(int i=0;i<=8;i++){ for(int j=0;j<=8;j++)printf("%d ",a[i][j]); puts(""); } return True; } int y=r[0]; //找到点最少的列 for(int i=r[0];i;i=r[i]) if(s[i]<s[y])y=i; remove(y); for(int i=d[y];i!=y;i=d[i]){ ans[dep]=row[i]; for(int j=r[i];j!=i;j=r[j]) remove(col[j]); if(dance(dep+1)) return True; for(int j=l[i];j!=i;j=l[j]) resume(col[j]); } resume(y); return False; } int main(){ n=729; m=324; init(); for(int i=0; i<9; i++){ //数独的行 for(int j=0,x; j<9; j++){ //数独的列 scanf("%d",&x);a[i][j]=x; for(int k=1; k<=9; k++){ //9个数 if(x0||x==k){ h=t=cnt; //每行的第一个点 int r=i99+j9+k; //数独→链表行 link(r,i9+j+1); link(r,811+i9+k); link(r,812+j9+k); link(r,813+(i/33+j/3)*9+k); } } } } dance(0); }</pre>

      //by:hansang 代码来源于 y总
      //注释里的方格指Sudoku中的196个小方格,大方格指16个大方格 
      //代码很长,分段记比较好 
      #include<bits/stdc++.h>
      using namespace std;
       
      const int N = 16;
       
      int Map[1 << N], ones[1 << N]; //Map[i]表示 i是2的几次方,ones[i]表示 i里面有几个1 
      int state[N][N]; //state[i][j]是一串二进制数,第 k位的1表示这个位置能填 k+'A'-1
      int bstate[N*N + 1][N][N], bstate2[N*N + 1][N][N]; //备份  
      char str[N][N + 1];
      char bstr[N*N + 1][N][N + 1];
       
      inline int lowbit(int x){return x&-x;} //是 x的二进制数中最低位的 1所对应的值
       
      void draw(int x, int y, int c)
      {
          str[x][y]='A'+c;
       
          for(int i=0; i<N; i++) //同行同列的数都不能填 'A'+c
          {
              state[x][i] &= ~(1 << c); //其他位都不变,第 c位如果是 1的话变成 0 
              state[i][y] &= ~(1 << c);
          }
       
          int sx=x/4*4, sy=y/4*4; //x y所在的大方格的左上角 
          //同一个方格的数都不能填 'A'+c
          for(int i=0; i<4; i++)
              for(int j=0; j<4; j++) 
                  state[sx+i][sy+j] &= ~(1 << c);
       
          state[x][y] = 1 << c; //这个方格填了 'A'+c
      }
       
      bool dfs(int cnt)
      {
          if (!cnt) return 1;
       
          int kcnt=cnt;
          memcpy(bstate[kcnt], state, sizeof state);
          memcpy(bstr[kcnt], str, sizeof str); //备份 
           
          for(int i=0; i<N; i++ )
              for(int j=0; j<N; j++ )
                  if(str[i][j]=='-')
                  {
                      if(!state[i][j]) // 每个空方格如果不能填则返回 0
                      {
                          memcpy(state, bstate[kcnt], sizeof state);
                          memcpy(str, bstr[kcnt], sizeof str); //拷回去
                          return 0;
                      }
       
                      if(ones[state[i][j]]==1)//如果只有一个选项,则直接填上
                      {
                          draw(i, j, Map[state[i][j]]);
                          cnt--;
                      }
                  }
       
          // 每一行如果某个字母不能填,则返回False;如果某个字母只有一种填法,则直接填
          for(int i=0; i<N; i++)
          {
              int sor=0, sand=(1<<N) - 1; //sor是一串二进制数,第 k位的1表示这行能填k-'A'+1 (判断用)    sand和sor一样 (计算用)
              int drawn=0; //drawn是一串二进制数,第 k位的1表示这行   *填了*   k-'A'+1
              for(int j=0; j<N; j++)
              {
                  int s = state[i][j];
                  sand &= ~(sor & s); //要是有以前的方格也能填和 str[i][j]一样能填的数,就在 sand中删去 
                  sor |= s; //str[i][j]能填的,这一行也能填 
       
                  if (str[i][j]!='-') drawn |= s;
              }
       
              if(sor!=(1 << N)-1) //正常来说,一行能填所有数,否则无解 
              {
                  memcpy(state, bstate[kcnt], sizeof state);
                  memcpy(str, bstr[kcnt], sizeof str);
                  return 0;
              }
       
              for(int j=sand; j; j-=lowbit(j)) //能填的先填 
              {
                  int t=lowbit(j);
                  if (!(drawn&t)) //没填过的 
                  {
                      for(int k=0; k<N; k++)
                          if(state[i][k]&t) //在第 k个位置上是 1 
                          {
                              draw(i, k, Map[t]);
                              cnt -- ;
                              break;
                          }
                  }
              }
          }
       
          // 每一列,如果某个字母不能填,则返回False;如果某个字母只有一种填法,则直接填
          //打完行的直接复制就好,注意 i j 交换和 k i 交换     (懒得打注释啦) 
          for(int i=0; i<N; i++)
          {
              int sor=0, sand=(1 << N)-1;
              int drawn=0;
              for(int j=0; j<N; j++)
              {
                  int s=state[j][i];
                  sand &= ~(sor & s);
                  sor|=s;
       
                  if (str[j][i] != '-') drawn |= s;
              }
       
              if(sor!=(1 << N)-1)
              {
                  memcpy(state, bstate[kcnt], sizeof state);
                  memcpy(str, bstr[kcnt], sizeof str);
                  return 0;
              }
       
              for(int j=sand; j; j-=lowbit(j))
              {
                  int t=lowbit(j);
                  if(!(drawn & t))
                  {
                      for(int k=0; k<N; k++)
                          if(state[k][i]&t)
                          {
                              draw(k, i, Map[t]);
                              cnt -- ;
                              break;
                          }
                  }
              }
          }
       
          // 每个16宫格,如果某个字母不能填,则返回False;如果某个字母只有一种填法,则直接填
          //打完行的直接复制就好,注意 i j 替换 sx+dx sy+dy,特殊情况 i k 替换 sx+dx sy+dy
          for(int i=0; i<N; i++)
          {
              int sor=0, sand=(1 << N)-1;
              int drawn=0;
              for(int j=0; j<N; j++)
              {
                  int sx=i/4 *4, sy=i%4 *4; //i所在的大方格的左上角 
                  int dx=j/4, dy=j%4; //每个九宫格里的相对位置 
                  int s=state[sx+dx][sy+dy];
                  sand &= ~(sor & s);
                  sor|=s;
       
                  if (str[ sx+dx ][ sy+dy ]!='-') drawn|=state[ sx+dx ][ sy+dy ];
              }
       
              if(sor!=(1<<N)-1)
              {
                  memcpy(state, bstate[kcnt], sizeof state);
                  memcpy(str, bstr[kcnt], sizeof str);
                  return 0;
              }
              for(int j=sand; j; j-=lowbit(j))
              {
                  int t=lowbit(j);
                  if(!(drawn&t))
                  {
                      for(int k=0; k<N; k++)
                      {
                          int sx=i/4 *4, sy=i%4 *4;
                          int dx=k/4, dy=k%4;
                          if(state[sx+dx][sy+dy] & t)
                          {
                              draw(sx+dx, sy+dy, Map[t]);
                              cnt -- ;
                              break;
                          }
                      }
                  }
              }
          }
       
          if (!cnt) return 1; //注意这里改成cnt==0可能过不了! 
       
          int x, y, s=100; 
          for(int i=0; i<N; i++) //寻找选择最少的方格 
              for(int j=0; j<N; j++)
                  if(str[i][j]=='-' && ones[state[i][j]]<s)
                  {
                      s=ones[state[i][j]];
                      x=i; y=j;
                  }
       
          memcpy(bstate2[kcnt], state, sizeof state);
          for(int i=state[x][y]; i; i-=lowbit(i)) //把还有选择的方格都过一遍 
          {
              memcpy(state, bstate2[kcnt], sizeof state); //每做一次就拷回去 
              draw(x, y, Map[lowbit(i)]);
              if(dfs(cnt-1)) return 1;
          }
       
          memcpy(state, bstate[kcnt], sizeof state);
          memcpy(str, bstr[kcnt], sizeof str);
          return 0;
      }
       
      int main()
      {
          for(int i=1; i<N; i++) Map[1<<i]=i; //2的i次方=i 
          for(int i=0; i<(1<<N); i++) for(int j=i; j; j-=lowbit(j)) ones[i]++; //i里面有几个1 
       
          while(cin>>str[0]) //用cin能避免读入空格 换行 
          {
              for(int i=1; i<N; i++) cin>>str[i];
       
              for(int i=0; i<N; i++)
                  for(int j=0; j<N; j++)
                      state[i][j]=(1<<N) - 1; //初始化为15个1 
       
              int cnt=0;
              for(int i=0; i<N; i++)
                  for(int j=0; j<N; j++)
                      if(str[i][j] != '-')draw(i, j, str[i][j]-'A'); //格子里有数就表记 
                      else cnt++; //有cnt个要填的格子 
       
              dfs(cnt);
              for(int i=0; i<N; i++) printf("%s\n",str[i]);
              printf("\n");
          }
          return 0;
      }
      • 1

      B32 Dancing Links 舞蹈链 *【递归】数独Ⅲ(spj)

      信息

      ID
      1084
      时间
      3000ms
      内存
      128MiB
      难度
      7
      标签
      递交数
      96
      已通过
      23
      上传者