用二进制优化常数卡过Pku3074 Sudoku

这道题看起来用暴力搜索似乎不可能过,但是因为二进制的运行速度实在是太快了。我们可以用二进制运算来代替布尔型的for循环枚举。这样,我们便可以节省大量时间。

我是用了l[i],h[i],kuai[i]分别代表第i列,第i行,第i块的状态,0代表未出现过,1代表已出现。

我还每次找可以填的数字最少的格子进行搜索,便把这题卡过去了。

接下来上代码:

const
 w:array[0..8,0..8]of longint=((0,0,0,1,1,1,2,2,2),
                              (0,0,0,1,1,1,2,2,2),
                              (0,0,0,1,1,1,2,2,2),
                              (3,3,3,4,4,4,5,5,5),
                              (3,3,3,4,4,4,5,5,5),
                              (3,3,3,4,4,4,5,5,5),
                              (6,6,6,7,7,7,8,8,8),
                              (6,6,6,7,7,7,8,8,8),
                              (6,6,6,7,7,7,8,8,8));
var
bb,aa:array[0..512]of longint;
a:array[0..8,0..8]of longint;
h,l,kui:array[0..8]of longint;
now,minn,x,y,ans,n,m,i,j,k:longint;
s:string;

procedure dfs(x,y:longint);
var
ii,jj,xx,yy,minn,i,j,k:longint;
begin
if (a[x,y]<>-1) then
  begin
    ans:=1;
    exit;
  end;
i:=1;
j:=l[y] or h[x] or kui[w[x,y]];
if j=511 then
  exit;
while i<512 do
begin
     if j or i<>j then
             begin
               l[y]:=l[y] or i;
               h[x]:=h[x] or i;
               kui[w[x,y]]:=kui[w[x,y]]or i;
               a[x,y]:=bb[i];
               minn:=16;
               xx:=0;yy:=0;
               ii:=0;
               while ii<=8 do
                begin
                 for jj:=0 to 8 do
                   if a[ii,jj]=-1 then
                    begin
                      k:=l[jj] or h[ii] or kui[w[ii,jj]];
                      if aa[k]<minn then
                        begin
                          minn:=aa[k];
                          xx:=ii; yy:=jj;
                        end;
                      if minn=1 then
                         begin
                           ii:=9;
                           break;
                         end;
                    end;
                    inc(ii);
                 end;
               dfs(xx,yy);
               if ans<>0 then
                 exit;
               l[y]:=l[y] and (not i);
               h[x]:=h[x] and (not i);
               kui[w[x,y]]:=kui[w[x,y]] and (not i);
             end;
           i:=i<<1;
end;
a[x,y]:=-1;
end;

begin
bb[1]:=0; bb[2]:=1; bb[4]:=2; bb[8]:=3; bb[16]:=4;
bb[32]:=5; bb[64]:=6; bb[128]:=7;bb[256]:=8;
for k:=0 to 512 do
  begin
          now:=0;
          if k or 1<>k then
            inc(now);
          if k or 2<>k then
            inc(now);
          if k or 4<>k then
            inc(now);
          if k or 8<>k then
            inc(now);
          if k or 16<>k then
            inc(now);
          if k or 32<>k then
            inc(now);
          if k or 64<>k then
            inc(now);
          if k or 128<>k then
            inc(now);
          if k or 256<>k then
            inc(now);
          aa[k]:=now;
  end;
while not eof do
  begin
    fillchar(h,sizeof(h),0);
    fillchar(l,sizeof(l),0);
    fillchar(kui,sizeof(kui),0);
    fillchar(a,sizeof(a),$ff);
    readln(s);    ans:=0;
    if s[1]='e' then
      break;
    for i:=0 to 80 do
      if  s[i+1]<>'.' then
        begin
          j:=ord(s[i+1])-ord('1');
          a[i div 9,i mod 9]:=j;
          h[i div 9]:=h[i div 9] or (1<<j);
          l[i mod 9]:=l[i mod 9] or (1<<j);
          kui[w[i div 9,i mod 9]]:=kui[w[i div 9,i mod 9]] or(1<<j);
        end;
    minn:=16;
    i:=0;
    while i<=8 do
      begin
      for j:=0 to 8 do
        if a[i,j]=-1 then
        begin
          k:=l[j] or h[i] or kui[w[i,j]];
          if aa[k]<minn then
            begin
              minn:=aa[k];
              x:=i; y:=j;
            end;
          if minn=1 then
            begin
              i:=9;
              break;
            end;
        end;
        inc(i);
      end;
    dfs(x,y);
    for i:=0 to 8 do
      begin
        for j:=0 to 8 do
          write(a[i,j]+1);
      end;
    writeln;
  end;
end.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值