位运算简介及实用技巧(四):实战篇

原文:http://www.matrix67.com/blog/archives/268布局

    下面分享的是我本身写的三个代码,里面有些题目也是我本身出的。这些代码都是在个人Pascal时代写的,恕不提供C语言了。代码写得并很差,我只是想告诉你们位运算在实战中的应用,包括了搜索和状态压缩DP方面的题目。其实你们能够在网上找到更多用位运算优化的题目,这里整理出一些本身写的代码,只是为了原创系列文章的完整性。这一系列文章到这里就结束了,但愿你们能有所收获。
    Matrix67原创,转贴请注明出处。优化

Problem : 费解的开关设计

题目来源
    06年NOIp模拟赛(一) by Matrix67 第四题code

问题描述
    你玩过“拉灯”游戏吗?25盏灯排成一个5×5的方形。每个灯都有一个开关,游戏者能够改变它的状态。每一步,游戏者能够改变某一个灯的状态。游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。
    咱们用数字“1”表示一盏开着的灯,用数字“0”表示关着的灯。下面这种状态对象

10111
01101
10111
10000
11011blog

    在改变了最左上角的灯的状态后将变成:游戏

01111
11101
10111
10000
11011内存

    再改变它正中间的灯后状态将变成:get

01111
11001
11001
10100
11011input

    给定一些游戏的初始状态,编写程序判断游戏者是否可能在6步之内使全部的灯都变亮。

输入格式
    第一行有一个正整数n,表明数据中共有n个待解决的游戏初始状态。
    如下若干行数据分为n组,每组数据有5行,每行5个字符。每组数据描述了一个游戏的初始状态。各组数据间用一个空行分隔。
    对于30%的数据,n<=5;
    对于100%的数据,n<=500。

输出格式
    输出数据一共有n行,每行有一个小于等于6的整数,它表示对于输入数据中对应的游戏状态最少须要几步才能使全部灯变亮。
    对于某一个游戏初始状态,若6步之内没法使全部灯变亮,请输出“-1”。

样例输入
3
00111
01011
10001
11010
11100

11101
11101
11110
11111
11111

01111
11111
11111
11111
11111

样例输出
3
2
-1

程序代码
const
   BigPrime=3214567;
   MaxStep=6;
type
   pointer=^rec;
   rec=record
         v:longint;
         step:integer;
         next:pointer;
       end;

var
   total:longint;
   hash:array[0..BigPrime-1]of pointer;
   q:array[1..400000]of rec;

function update(a:longint;p:integer):longint;
begin
   a:=a xor (1 shl p);
   if p mod 5<>0 then a:=a xor (1 shl (p-1));
   if (p+1) mod 5<>0 then a:=a xor (1 shl (p+1));
   if p<20 then a:=a xor (1 shl (p+5));
   if p>4 then a:=a xor (1 shl (p-5));
   exit(a);
end;

function find(a:longint;step:integer):boolean;
var
   now:pointer;
begin
   now:=hash[a mod BigPrime];
   while now<>nil do
   begin
      if now^.v=a then exit(true);
      now:=now^.next;
   end;

   new(now);
   now^.v:=a;
   now^.step:=step;
   now^.next:=hash[a mod BigPrime];
   hash[a mod BigPrime]:=now;
   total:=total+1;
   exit(false);
end;

procedure solve;
var
   p:integer;
   close:longint=0;
   open:longint=1;
begin
   find(1 shl 25-1,0);
   q[1].v:=1 shl 25-1;
   q[1].step:=0;
   repeat
      inc(close);
      for p:=0 to 24 do
         if not find(update(q[close].v,p),q[close].step+1) and (q[close].step+1<MaxStep) then
         begin
            open:=open+1;
            q[open].v:=update(q[close].v,p);
            q[open].step:=q[close].step+1;
         end;
   until close>=open;
end;

procedure print(a:longint);
var
   now:pointer;
begin
   now:=hash[a mod BigPrime];
   while now<>nil do
   begin
      if now^.v=a then
      begin
         writeln(now^.step);
         exit;
      end;
      now:=now^.next;
   end;
   writeln(-1);
end;

procedure main;
var
   ch:char;
   i,j,n:integer;
   t:longint;
begin
   readln(n);
   for i:=1 to n do
   begin
      t:=0;
      for j:=1 to 25 do
      begin
         read(ch);
         t:=t*2+ord(ch)-48;
         if j mod 5=0 then readln;
      end;
      print(t);
      if i<n then readln;
   end;
end;

begin
   solve;
   main;
end.

=======================  性感的分割线  =======================

Problem : garden / 和MM逛花园

题目来源
    07年Matrix67生日邀请赛第四题

问题描述
    花园设计强调,简单就是美。Matrix67常去的花园有着很是简单的布局:花园的全部景点的位置都是“对齐”了的,这些景点能够看做是平面坐标上的格点。相邻的景点之间有小路相连,这些小路所有平行于坐标轴。景点和小路组成了一个“不完整的网格”。
    一个典型的花园布局如左图所示。花园布局在6行4列的网格上,花园的16个景点的位置用红色标注在了图中。黑色线条表示景点间的小路,其他灰色部分实际并不存在。
        

    Matrix67 的生日那天,他要带着他的MM在花园里游玩。Matrix67不会带MM两次通过同一个景点,所以每一个景点最多被游览一次。他和他MM边走边聊,他们是 如此的投入以至于他们从不会“主动地拐弯”。也就是说,除非前方已没有景点或是前方的景点已经访问过,不然他们会一直往前走下去。当前方景点不存在或已游 览过期,Matrix67会带MM另选一个方向继续前进。因为景点个数有限,访问过的景点将愈来愈多,早晚会出现不能再走的状况(即四个方向上的相邻景点 都访问过了),此时他们将结束花园的游览。Matrix67但愿知道以这种方式游览花园是否有可能遍历全部的景点。Matrix67能够选择从任意一个景 点开始游览,以任意一个景点结束。
  在上图所示的花园布局中,一种可能的游览方式如右图所示。这种浏览方式从(1,2)出发,以(2,4)结束,通过每一个景点刚好一次。
  输入格式
  第一行输入两个用空格隔开的正整数m和n,表示花园被布局在m行n列的网格上。
  如下m行每行n个字符,字符“0”表示该位置没有景点,字符“1”表示对应位置有景点。这些数字之间没有空格。
  输出格式
  你的程序须要寻找知足“不主动拐弯”性质且遍历全部景点的游览路线。
  若是没有这样的游览路线,请输出一行“Impossible”(不带引号,注意大小写)。
  若是存在游览路线,请依次输出你的方案中访问的景点的坐标,每行输出一个。坐标的表示格式为“(x,y)”,表明第x行第y列。
  若是有多种方案,你只须要输出其中一种便可。评测系统能够判断你的方案的正确性。
  样例输入
  6 4
  1100
  1001
  1111
  1100
  1110
  1110
  样例输出
  (1,2)
  (1,1)
  (2,1)
  (3,1)
  (4,1)
  (5,1)
  (6,1)
  (6,2)
  (6,3)
  (5,3)
  (5,2)
  (4,2)
  (3,2)
  (3,3)
  (3,4)
  (2,4)
  数据规模
  对于30%的数据,n,m<=5;
  对于100%的数据,n,m<=10。 
  </BLOCKQUOTE>
  程序代码:
  <CODE>program garden;
  const
  dir:array[1..4,1..2]of integer=
  ((1,0),(0,1),(-1,0),(0,-1));
  type
  arr=array[1..10]of integer;
  rec=record x,y:integer;end;
  var
  map:array[0..11,0..11]of boolean;
  ans:array[1..100]of rec;
  n,m,max:integer;
  step:integer=1;
  state:arr;
  procedure readp;
  var
  i,j:integer;
  ch:char;
  begin
  readln(m,n);
  for i:=1 to n do
  begin
  for j:=1 to m do
  begin
  read(ch);
  map[i,j]:=(ch='1');
  inc(max,ord( map[i,j] ))
  end;
  readln;
  end;
  end;
  procedure writep;
  var
  i:integer;
  begin
  for i:=1 to step do
  writeln( '(' , ans.x , ',' , ans.y , ')' );
  end;
  procedure solve(x,y:integer);
  var
  tx,ty,d:integer;
  step_cache:integer;
  state_cache:arr;
  begin
  step_cache:=step;
  state_cache:=state;
  if step=max then
  begin
  writep;
  exit;
  end;
  for d:=1 to 4 do
  begin
  tx:=x+dir[d,1];
  ty:=y+dir[d,2];
  while map[tx,ty] and ( not state[tx] and(1 shl (ty-1) )>0) do
  begin
  inc(step);
  ans[step].x:=tx;
  ans[step].y:=ty;
  state[tx]:=state[tx] or ( 1 shl (ty-1) );
  tx:=tx+dir[d,1];
  ty:=ty+dir[d,2];
  end;
  tx:=tx-dir[d,1];
  ty:=ty-dir[d,2];
  if (tx<>x) or (ty<>y) then solve(tx,ty);
  state:=state_cache;
  step:=step_cache;
  end;
  end;
  {====main====}
  var
  i,j:integer;
  begin
  assign(input,'garden.in');
  reset(input);
  assign(output,'garden.out');
  rewrite(output);
  readp;
  for i:=1 to n do
  for j:=1 to m do
  if map[i,j] then
  begin
  ans[1].x:=i;
  ans[1].y:=j;
  state:=1 shl (j-1);
  solve(i,j);
  state:=0;  end;  close(input);  close(output);  end.</CODE> 
  对C语言中位运算的一点补充:(位数不一样的运算数之间的运算规则)-  C语言中,位运算的对象能够是整型(int)和字符型(char)数据。(整形数据能够直接转化成二进制数,字符型数据在内存中以它的ASCII码值存放,也能够站化成二进制数)当两个运算数类型不一样时,位数亦会不一样。遇到这种状况,系统将自动进行以下处理:  1将两个运算数右端对齐。  2 再将位数短的一个运算数往高位扩充,即:无符号数和正整数左侧用0补全;负数左侧用1补全;而后对位数相等的两个运算数,按位进行运算。

相关文章
相关标签/搜索