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

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

Problem : 费解的开关 

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

问题描述 
  你玩过“拉灯”游戏吗?25盏灯排成一个5x5的方形。每一个灯都有一个开关,游戏者可以改变它的状态。每一步,游戏者可以改变某一个灯的状态。游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。 
  我们用数字“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”。 

样例输入 

00111 
01011 
10001 
11010 
11100 

11101 
11101 
11110 
11111 
11111 

01111 
11111 
11111 
11111 
11111 

样例输出 


-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。 

 



程序代码:
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[i].x , ',' , ans[i].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[i]:=1 shl (j-1);
      solve(i,j);
      state[i]:=0;
   end;

 close(input);
 close(output);
end.

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


Problem : cowfood / 玉米地 

题目来源 
  USACO月赛 

问题描述 
  农夫约翰购买了一处肥沃的矩形牧场,分成M*N(1<=M<=12; 1<=N<=12)个格子。他想在那里的一些格子中种植美味的玉米。遗憾的是,有些格子区域的土地是贫瘠的,不能耕种。 
  精明的约翰知道奶牛们进食时不喜欢和别的牛相邻,所以一旦在一个格子中种植玉米,那么他就不会在相邻的格子中种植,即没有两个被选中的格子拥有公共边。他还没有最终确定哪些格子要选择种植玉米。 
  作为一个思想开明的人,农夫约翰希望考虑所有可行的选择格子种植方案。由于太开明,他还考虑一个格子都不选择的种植方案!请帮助农夫约翰确定种植方案总数。 

输入格式: 
  第一行:两个用空格分隔的整数M和N 
  第二行到第M+1行:第i+1行描述牧场第i行每个格子的情况,N个用空格分隔的整数,表示这个格子是否可以种植(1表示肥沃的、适合种植,0表示贫瘠的、不可种植) 

输出格式 
  一个整数,农夫约翰可选择的方案总数除以 100,000,000 的余数 

样例输入 
2 3 
1 1 1 
0 1 0 

样例输出 


样例说明 

  给可以种植玉米的格子编号: 
    1 2 3 
      4 

  只种一个格子的方案有四种(1,2,3或4),种植两个格子的方案有三种(13,14或34),种植三个格子的方案有一种(134),还有一种什么格子都不种。 
  4+3+1+1=9。 

数据规模 
  对于30%的数据,N,M<=4; 
  对于100%的数据,N,M<=12。 

 



程序代码:
program cowfood;

const
 d=100000000;
 MaxN=12;

var
 f:array[0..MaxN,1..2000]of longint;
 w:array[1..2000,1..2000]of boolean;
 st:array[0..2000]of integer;
 map:array[0..MaxN]of integer;
 m,n:integer;

function Impossible(a:integer):boolean;
var
 i:integer;
 flag:boolean=false;
begin
 for i:=1 to MaxN do
 begin
    if flag and (a and 1=1) then exit(true);
    flag:=(a and 1=1);
    a:=a shr 1;
 end;
 exit(false);
end;

function Conflict(a,b:integer):boolean;
var
 i:integer;
begin
 for i:=1 to MaxN do
 begin
    if (a and 1=1) and (b and 1=1) then exit(true);
    a:=a shr 1;
    b:=b shr 1;
 end;
 exit(false);
end;

function CanPlace(a,b:integer):boolean;
begin
 exit(a or b=b);
end;

procedure FindSt;
var
 i:integer;
begin
 for i:=0 to 1 shl MaxN-1 do
    if not Impossible(i) then
    begin
       inc(st[0]);
       st[st[0]]:=i;
    end;
end;

procedure Init;
var
 i,j:integer;
begin
 for i:=1 to st[0] do
 for j:=i to st[0] do
    if not Conflict(st[i],st[j]) then
    begin
       w[i,j]:=true;
       w[j,i]:=true;
    end;
end;

procedure Readp;
var
 i,j,t,v:integer;
begin
 readln(m,n);
 for i:=1 to m do
 begin
    v:=0;
    for j:=1 to n do
    begin
       read(t);
       v:=v*2+t;
    end;
    map[i]:=v;
    readln;
 end;
end;

procedure Solve;
var
 i,j,k:integer;
begin
 f[0,1]:=1;
 map[0]:=1 shl n-1;
 for i:=1 to m do
 for j:=1 to st[0] do
    if not CanPlace(st[j],map[i]) then f[i,j]:=-1 else
      for k:=1 to st[0] do if (f[i-1,k]<>-1) and w[j,k] then
         f[i,j]:=(f[i,j]+f[i-1,k]) mod d;
end;

procedure Writep;
var
 j:integer;
 ans:longint=0;
begin
 for j:=1 to st[0] do
    if f[m,j]<>-1 then ans:=(ans+f[m,j]) mod d;
 writeln(ans);
end;

begin
 assign(input,'cowfood.in');
 reset(input);
 assign(output,'cowfood.out');
 rewrite(output);

 FindSt;
 Init;
 Readp;
 Solve;
 Writep;

 close(input);
 close(output);
end.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值