位運算簡介及實用技巧(四):實戰篇

原文: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補全;而後對位數相等的兩個運算數,按位進行運算。

相關文章
相關標籤/搜索