• POJ 3074 Sudoku【DancingLinks,数独】



    http://poj.org/problem?id=3074
    POJ 3074 Sudoku
    大意:求解规模为9*9的数独问题
    分析:对于每一行,每一列,每一宫,数字1,2。。。9都需出现1次且只能是一次,
    可转化为精确覆盖问题,用跳舞链解决
    建模型:
    行数为9*9*9,数独中,第i行j列放数字k的状态存储在图中第(i*9+j)*9+k行中
    列数为9*9+9*9+9*9+9*9,

    其中第一个9*9代表第i格是否已填满,
    用第二个9*9确保每行的数字唯一且均出现一次
    第三个9*9确保每列的数字唯一且出现一次
    第四个9*9确保每宫的数字唯一出现1次

    #include<stdio.h>
    #include<string.h>
    const int MAX_COLOUMN = 81+9*9+9*9+9*9+2;//最多出现列数
    const int MAX_ROW = 81*9+2;//最多出现的列数
    
    int cnt[MAX_COLOUMN];//cnt[i]统计第i列1的个数
    int most,coloumn;
    bool ans[MAX_ROW];//ans存放最终选中的行
    //跳舞链中的节点
    struct Point
    {
       int up,down,left,right;//上,下,左,右
       int coloumn;//该点所在的列标
       int row;//行标
    }node[MAX_ROW*MAX_COLOUMN+MAX_COLOUMN];
    
    //初始化跳舞链信息为空
    void init(int m)
    {
     int i;
     for(i=0;i<=m;i++)
     {
      node[i].down=i;
      node[i].up = i;
      node[i].coloumn=i;
      node[i].left=i-1;
      node[i].right=i+1;
      cnt[i]=0;
     }
     node[0].left = m;
     node[m].right = 0;
    }
    
    void remove(int c)//删除c列上所有1元素所在的行
    {
     node[node[c].right].left=node[c].left;
     node[node[c].left].right=node[c].right;
     int t,tt;
     for(t=node[c].down;t!=c;t=node[t].down)//从上到下从左到右删除该列上的每一非零元素所在行信息
     {
      for(tt = node[t].right;tt!=t;tt=node[tt].right)//删除非零元素所在行
      {
                cnt[node[tt].coloumn]--;
       node[node[tt].down].up = node[tt].up;
       node[node[tt].up].down = node[tt].down;
      }
     }
    }
    
    void resume(int c)//还原c列上所有1元素所在的行
    {
     int t,tt;
     for(t=node[c].up;t!=c;t=node[t].up)//从下往上从左到右还原该c列中1所在的行信息
     {
      for(tt=node[t].left;tt!=t;tt=node[tt].left)
      {
       cnt[node[tt].coloumn]++;
       node[node[tt].up].down=tt;
       node[node[tt].down].up=tt;
      }
     }
    
     node[node[c].right].left=c;
     node[node[c].left].right=c;
    }
    
    bool dfs(int k)//k为已经选中的行的数目
    {
     int i,j;
     if(k>=most)return false;
     if(node[coloumn].right == coloumn)//当前跳舞链已为空
     {
      if(k<most)
       most = k;
      return true;
     }
    
     int t = coloumn+1;
     int c;
     //选取当前矩阵中1最少的列
     for(i=node[coloumn].right;i!=coloumn;i=node[i].right)
     {
      if(cnt[i]<t)
      {
       c=i;t=cnt[i];
       if(t==1)break;
      }
     }
        
     remove(c);//删除列c中所有1所在的行
    
     //删除时从左到右从上到下,还原时从下到上,从右到左
     for(i = node[c].down;i!=c;i=node[i].down)
     {
      for(j=node[i].right;j!=i;j=node[j].right)
      {
       remove(node[j].coloumn);
      }
      ans[node[j].row]=true;
      if(dfs(k+1))
      {
       return true;
      }
      ans[node[j].row]=false;
      for(j=node[j].left;j!=i;j=node[j].left)
      {
       resume(node[j].coloumn);
      }
    
      
     }
    
     resume(c);
     return false;
    }
    bool graph[MAX_ROW][MAX_COLOUMN];
    void addrow(int i,int j,int k)
    {
        int curr = (i*9+j)*9+k;
     graph[curr][(i*9+j)]=true;
     graph[curr][81+i*9+k]=true;
     graph[curr][81+81+j*9+k]=true;
        int tr = i/3;
     int tc = j/3;
     graph[curr][81+81+81+(tr*3+tc)*9+k]=true;
    }
    
    char str[MAX_ROW];
    int main()
    {
     int N,M,i,j,k;
     while(scanf("%s",str)!=EOF)
     {
      if(strcmp(str,"end")==0)break;
      N=81*9;
      M = 9*9+9*9+9*9+9*9;
      coloumn = M;
      int cur=coloumn+1;//当前节点编号
      init(coloumn);
      memset(graph,0,sizeof(graph));
      for(i=0;i<9;i++)
       for(j=0;j<9;j++)
       {
        if(str[i*9+j]=='.')
        {
         for(k=0;k<9;k++)//遍历每一种颜色
         {
           addrow(i,j,k);
         }
         continue;
        }
        k = str[i*9+j]-'1';
           addrow(i,j,k);
       }
      for(i=0;i<N;i++)
      {
       int start = cur;//记录第i列的开始点编号
       int pre = cur;//记录该列中当前1的左边第一个1编号
       for(j=0;j<M;j++)
       {
       // scanf("%d",&n);
        if(graph[i][j])//跳舞链中仅插入非0元素
        {
         int pos = j;
         node[cur].up = node[pos].up;
         node[node[pos].up].down = cur;
            node[cur].down = pos;
         node[pos].up = cur;
         cnt[pos]++;//该列1的个数+1
         node[cur].coloumn = pos;
         node[cur].left = pre;
         node[pre].right = cur;
         node[cur].right = start;
                        node[start].left=cur;
         node[cur].row = i;
         pre=cur++;
        }
       }
      }
    
     
      most = N+1;//记录最少需要选中的行数
      memset(ans,false,sizeof(ans));
      dfs(0);
       // printf("Yes, I found it\n");
      for(i=0;i<81;i++)
       for(j=0;j<9;j++)
        if(ans[i*9+j])
        {
         printf("%d",j+1);
         break;
        }
    
        printf("\n");
     
     }
     return 0;
    }
    
  • 相关阅读:
    c#反射动态创建窗体
    ImageSwitcher 图片切换器
    viewSwitcher 切换视图
    ratingBar 星级评分条
    seekBar拖动滑块
    pythonUDP发送结构体,对齐到C++结构体
    pyqt5界面
    progressbar
    SVN服务器搭建和使用(一)
    关于MBR、GPT以及BIOS引导模式区分
  • 原文地址:https://www.cnblogs.com/AndreMouche/p/1964136.html
Copyright © 2020-2023  润新知