NOIP2014 提高組 生活大爆炸版石頭剪刀布

NO IMAGE

描述

石頭剪刀布是常見的猜拳遊戲:石頭勝剪刀,剪刀勝布,布勝石頭。如果兩個人出拳一 樣,則不分勝負。在《生活大爆炸》第二季第 8 集中出現了一種石頭剪刀布的升級版遊戲。 升級版遊戲在傳統的石頭剪刀布遊戲的基礎上,增加了兩個新手勢:

斯波克:《星際迷航》主角之一。 蜥蜴人:《星際迷航》中的反面角色。

這五種手勢的勝負關係如表一所示,表中列出的是甲對乙的遊戲結果。

圖片

現在,小 A 和小 B 嘗試玩這種升級版的猜拳遊戲。已知他們的出拳都是有週期性規律的,但週期長度不一定相等。例如:如果小 A 以“石頭-布-石頭-剪刀-蜥蜴人-斯波克”長度 為 6 的週期出拳,那麼他的出拳序列就是“石頭-布-石頭-剪刀-蜥蜴人-斯波克-石頭-布-石頭-剪刀-蜥蜴人-斯波克-……”,而如果小 B 以“剪刀-石頭-布-斯波克-蜥蜴人”長度為 5 的周 期出拳,那麼他出拳的序列就是“剪刀-石頭-布-斯波克-蜥蜴人-剪刀-石頭-布-斯波克-蜥蜴人-……”

已知小 A 和小 B 一共進行 N 次猜拳。每一次贏的人得 1 分,輸的得 0 分;平局兩人都 得 0 分。現請你統計 N 次猜拳結束之後兩人的得分。

格式

輸入格式

第一行包含三個整數:N,NA,NB,分別表示共進行 N 次猜拳、小 A 出拳的週期長度, 小 B 出拳的週期長度。數與數之間以一個空格分隔。

第二行包含 NA 個整數,表示小 A 出拳的規律,第三行包含 NB 個整數,表示小 B 出拳 的規律。其中,0 表示“剪刀”,1 表示“石頭”,2 表示“布”,3 表示“蜥蜴人”, 4 表示 “斯波克”。數與數之間以一個空格分隔。

輸出格式

輸出一行, 包含兩個整數,以一個空格分隔,分別表示小 A、小 B 的得分。

樣例1

樣例輸入1[複製]

10 5 6
0 1 2 3 4
0 3 4 2 1 0

樣例輸出1[複製]

6 2

樣例2

樣例輸入2[複製]

9 5 5
0 1 2 3 4
1 0 3 2 4

樣例輸出2[複製]

4 4

限制

對於 100%的資料,0 < N ≤ 200,0 < NA ≤ 200, 0 < NB ≤ 200。

解題思路:

          看到這個題,首先想到的一定是模擬,再看一下資料規模,模擬可以實現,於是開始動手。如何模擬呢?題目最終要求兩人得分,而給定的是兩人出拳週期,並且題中告訴我們每一次贏的人得1分;輸的得0分;平局兩人都得0分,所以模擬的關鍵是判斷兩人每次出拳的勝負情況,從而求出最終得分。那麼問題又來了,如何判斷兩人勝負情況呢?連用if當然可以,用case自然也行,但這既費時又費力,還容易錯。所以我們需要一種快速判斷兩人勝負的方法。因為五種手勢之間的勝負關係是一定的,所以我們可以做一個勝負得分關係表,從而一勞永逸,搞定所有出拳情況。

          關係表製作方法如下:

第一步:找到題中所給勝負關係表。

第二步:將陰影部分翻譯出來。(要注意上圖是甲對乙的勝負關係)

第三步:將勝負關係變成得分情況。

0

0

1

1

0

1

0

0

1

0

0

1

0

0

1

0

0

1

0

1

1

1

0

0

0

第四步:將上表打程序序裡,就可以使用啦。最後還有一點要注意,

這個表代表的是第一個人(甲)的得分情況,而乙的得分情況與甲恰恰相反,在使用時將兩人出拳順序換一下即可(當然你硬要打兩個表我也不攔著你)。

程式碼詳解:

program rps;

const

  m:array[0..4,0..4] of integer=((0,0,1,1,0),

                          (1,0,0,1,0),

                          (0,1,0,0,1),

                          (0,0,1,0,1),

                          (1,1,0,0,0));

//得分情況表。

var

  a,b:array[1..300] of integer;//記錄小A、小B出拳順序。

  n,na,nb,i,aa,ab,ia,ib:integer;

//n、na、nb分別記錄次數,i給遊戲進行次數計數,aa、ab記錄兩人分數,ia、ib用於給兩人出拳計數。

procedure init;//讀入資料。

begin

  readln(n,na,nb);//分別讀入次數。

  for i:=1 to na do

    read(a[i]);

  for i:=1 to nb do

    read(b[i]); //將兩人出拳週期存在陣列中。

end;

{main}

begin

  assign(input,’rps.in’);

  assign(output,’rps.out’);

  reset(input);

  rewrite(output);//檔案。

  init;//讀入。

  aa:=0;

  ab:=0;

  ia:=1;

  ib:=1;//初始化變數。

  for i:=1 to n do//模擬出拳過程。

    begin

      aa:=aa m[a[ia],b[ib]];//根據分數表m算出小A得分。

      ab:=ab m[b[ib],a[ia]];//交換位置,算出小B得分。

      inc(ia);

      inc(ib);//兩人出拳次數加一。

      if ia>na then

        ia:=1;

      if ib>nb then

        ib:=1;//如果有人一個出拳週期完成,再從頭開始。

    end;

  write(aa,’ ‘);

  writeln(ab);//輸出兩人最終分數。

  close(input);

  close(output);//檔案

end.

程式碼2:

const
  p:array[0..4,0..4] of longint=
  ((0,0,1,1,0),
  (1,0,0,1,0),
  (0,1,0,0,1),
  (0,0,1,0,1),
  (1,1,0,0,0));
var
  n,na,nb:longint;
  ia,ib,ansa,ansb,j:longint;
  a,b:array[1..200] of longint;
begin
  //assign(input,’rps.in’);
//assign(output,’rpa.out’);
//reset(input);
//rewrite(output);
  readln(n,na,nb);
  for ia:=1 to na do
    read(a[ia]);
  for ib:=1 to nb do
    read(b[ib]);
  ia:=0;
  ib:=0;
  ansa:=0;
  ansb:=0;
  j:=0;
  repeat
    ia:=ia mod na 1;
    ib:=ib mod nb 1;
    ansa:=ansa p[a[ia],b[ib]];
    ansb:=ansb p[b[ib],a[ia]];
    inc(j);
  until j=n;
  writeln(ansa,’ ‘,ansb);
 // close(input);
//close(output);
  end.