zoukankan      html  css  js  c++  java
  • 位运算简介及实用技巧(四):实战篇

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

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

    Problem : 费解的开关

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

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

    10111
    01101
    10111
    10000
    11011

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

    01111
    11101
    10111
    10000
    11011

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

    01111
    11001
    11001
    10100
    11011

        给定一些游戏的初始状态,编写程序判断游戏者是否可能在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补全;然后对位数相等的两个运算数,按位进行运算。

  • 相关阅读:
    数据库Mysql给用户赋予操作表的权限
    C# log4net日志分等级打日志
    C# 将字符串转为函数名
    C# winform无法拖动控件
    C# 程序获取管理员方法
    C# 生成程序目录避免生成多余的XML和pdb
    C# 快速获取一个月的天数或最后一天
    正则
    C# 根据服务名打开所在文件夹
    330 div+css Experience
  • 原文地址:https://www.cnblogs.com/CCJVL/p/10894678.html
Copyright © 2011-2022 走看看