【2018杭电多校Round 4 J】 Let Sudoku Rotate

题意

一个16×16的数独(每一行,每一列都是包含了'0' ~ 'F' 16个字符),将矩阵分成4×4块,每块4×4,旋转其中一些方块
现在给你旋转后的矩阵,问最少顺时针旋转其中某些块多少次,能够还原成一个完整的数独

分析

这个题的精髓在于设置状态。
用整数中的二进制位位表示某一个数字有没有出现,
由于旋转只针对于4×4的矩阵,所以我们将4×4的子矩阵设为元素,一共16个元素,每个元素维护一下4种旋转情况下,每一行和每一列的二进制标志位(4个1,12个0)
从第一块开始深搜每一个元素的4种状态,得到最小答案
两个剪枝地方:如果当前旋转次数大于当前维护的最小状态,返回 因为是从左到右从上到下填充,所以如果上面的某一行不满足一列包含16个不同字符,那么直接返回

代码

#include<bits/stdc++.h>
using namespace std;
struct cre{
    int heng[4],shu[4];
}a[4][4][4];
char maps[20][20];
int H[16],S[16];
const int pp = (1<<16)-1;
int zh(char c)
{
    if(c<='9'&& c>='0') return c-'0';
    else return c-'A'+10;
}
void cl(int sx,int sy,int x,int y)
{
    for(int i = sx,id=0;i<sx+4;i++,id++)
    for(int j = sy;j<sy+4;j++)
        a[0][x][y].heng[id] |= 1<<(zh(maps[i][j]));
    for(int j = sy,id=0;j<sy+4;j++,id++)
    for(int i = sx;i<sx+4;i++)
        a[0][x][y].shu[id] |= 1<<(zh(maps[i][j]));
    for(int i = 1;i<4;i++){
    a[i][x][y].heng[0] = a[i-1][x][y].shu[0];
    a[i][x][y].heng[1] = a[i-1][x][y].shu[1];
    a[i][x][y].heng[2] = a[i-1][x][y].shu[2];
    a[i][x][y].heng[3] = a[i-1][x][y].shu[3];
    a[i][x][y].shu[0] = a[i-1][x][y].heng[3];
    a[i][x][y].shu[1] = a[i-1][x][y].heng[2];
    a[i][x][y].shu[2] = a[i-1][x][y].heng[1];
    a[i][x][y].shu[3] = a[i-1][x][y].heng[0];
    }
}

int ans = 0x7fffffff;
bool pd()
{
    for(int i = 0;i<16;i++) if(H[i]!=pp || S[i]!=pp) return false;
    return true;
}
void dg(int X,int Y,int tot)
{
    if(ans<tot) return;
    if(X && Y == 0){
    for(int i = 4*(X-1);i<4*X;i++){
        if(H[i]!=pp) return ;
    }
    }
    if(X == 4){
    if(pd()) ans = tot;
    return ;    
    }
    int NX =X ,NY = Y+1;
    if(NY == 4) NY = 0,NX++;
     for(int now =0 ;now<=3;now++)
    {
    for(int i = X*4,id = 0;i<X*4+4;i++,id++) H[i]^=a[now][X][Y].heng[id];
    for(int i = Y*4,id = 0;i<Y*4+4;i++,id++) S[i]^=a[now][X][Y].shu[id];
    dg(NX,NY,tot+now);
    for(int i = X*4,id = 0;i<X*4+4;i++,id++) H[i]^=a[now][X][Y].heng[id];
    for(int i = Y*4,id = 0;i<Y*4+4;i++,id++) S[i]^=a[now][X][Y].shu[id];
    }

}
int main()
{
       int _;scanf("%d",&_);
       while(_--)
       {
       memset(a,0,sizeof(a));
       memset(H,0,sizeof(H));
       memset(S,0,sizeof(S));
       ans = 0x7fffffff;
       for(int i =0;i<16;i++)
           for(int j = 0;j<16;j++)
           scanf(" %c",&maps[i][j]);
       for(int i = 0,ii=0;i<16;i+=4,ii++)
           for(int j = 0,jj=0;j<16;j+=4,jj++)
           cl(i,j,ii,jj);
        dg(0,0,0);  
        printf("%d\n",ans);
       }
}

转载于:https://www.cnblogs.com/greenty1208/p/9405074.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值