1. 程式人生 > >【刷題】LOJ 2863 「IOI2018」組合動作

【刷題】LOJ 2863 「IOI2018」組合動作

sample tst ring 註意 數據 -i swe 過程 格式

題目描述

你在玩一個動作遊戲。遊戲控制器有 \(4\) 個按鍵,ABXY。在遊戲中,你用組合動作來賺金幣。你可以依次按這些按鍵來完成一個組合動作。 這個遊戲有一個隱藏的按鍵序列,可以表示為由這 \(4\) 個字符組成的串 \(S\) 。你並不知道這個串 \(S\) ,但是你知道它的長度為 \(N\)

你還知道,\(S\) 的首字符不會在串中重復出現。 例如,\(S\) 可以是“ABXYY”或者“XYYAA”,但不能是“AAAAA”或“BXYBX”。

你可以依次按最多 \(4N\) 個按鍵來完成一個組合動作。串 \(p\) 為你所按的按鍵序列。你用這個組合動作賺到的金幣數量,等於同時為 \(p\)

之子串和 \(S\) 之前綴的最長字符串的長度。串 \(t\) 的子串定義為 \(t\) 中的連續字符序列(可以為空)。\(t\) 的前綴定義為 \(t\) 的子串,其或者為空,或者包含 \(t\) 的首字符。

例如,如果 \(S\) 是“ABXYY”,而 \(p\) 是“XXYYABYABXAY”,你會得到 \(3\) 個金幣,因為“ABX”是可作為 \(p\) 的子串的 \(S\) 的前綴中最長的。

你的任務是,用少量的組合動作,找出隱藏字符串 \(S\)

輸入格式

你需要實現下面的函數:

string guess_sequence(int N)
  • N:串 \(S\) 的長度。
  • 對每個測試用例,該函數被調用恰好一次。
  • 該函數應返回串 \(S\)

你的程序可以調用下面的函數:

int press(string p)
  • p:你的按鍵序列。
  • p 必須是長度為從 \(0\)\(4N\) 的串(包括 \(0\)\(4N\))。p 的每個字符必須是 ABX 或者 Y
  • 對每個測試用例,你調用該函數的次數不能超過 \(8\ 000\) 次。
  • 該函數的返回結果是,當按出按鍵序列 p 後你賺到的金幣數量。

如果不滿足上面的條件,你的程序將被判為 Wrong Answer。否則,你的程序將被判為 Accepted,而你的得分將根據 press 的調用次數來計算(參見子任務)。

樣例

\(S\) 為“ABXYY

”。評測程序調用了 guess_sequence(5)。數據交互過程的例子如下所示:

調用 返回值
press("XXYYABYABXAY") $3$
press("ABXYY") $5$
press("ABXYYABXYY") $5$
press("") $0$
press("X") $0$
press("BXYY") $0$
press("YYXBA") $1$
press("AY") $1$

對於 press 的第 \(1\) 次調用,“ABX”是“XXYYABYABXAY”的子串,而“ABXY”不是,因此返回 \(3\)

對於 press 的第 \(3\) 次調用,“ABXYY”本身是“ABXYYABXYY”的子串,因此返回 \(5\)

對於 press 的第 \(6\) 次調用,除了空串以外沒有“ABXYY”的其他前綴可以是“BXYY”的子串,因此返回 \(0\)

最後,guess_sequence(5) 應當返回“ABXYY”。

附件壓縮包中的文件 sample-01-in.txt 對應於本例。

數據範圍與提示

  • \(1\le N\le 2\ 000\)
  • \(S\) 的每個字符必須是 ABXY
  • \(S\) 的首字符不會再 \(S\) 中重復出現。

在本題中,評測程序不是適應性的。意思是說,在評測程序開始運行的時候 \(S\) 就固定下來,而且不依賴於你的程序所做的詢問。

子任務

  1. (5分)\(N=3\)
  2. (95分)沒有附加限制。對該子任務,你在每個測試用例上的得分將計算如下。設 \(q\) 為調用 press 的次數。
    • 如果 \(q\le N+2\),你的得分為 \(95\)
    • 如果 \(N+2<q\le N+10\),你的得分為 \(95-3(q-N-2)\)
    • 如果 \(N+10<q\le 2N+1\),你的得分為 \(25\)
    • 如果 \(\max\{N+10,2N+1\}<q\le 4N\),你的得分為 \(5\)
    • 否則,你的得分為 \(0\)

註意,你在每個子任務上的得分,等於你在該子任務下所有測試用例上的最低得分。

評測程序示例

評測程序示例將讀取如下格式的輸入:

  • \(1\) 行:\(S\)

如果你的程序被判為 Accepted,評測系統示例將打印出 Accepted: q,這裏 q 為函數 press 的調用次數。

如果你的程序被判為 Wrong Answer,它打印出 Wrong Answer: MSG。各類 MSG 的含義如下:

  • invalid press:輸入到 press 的值 p 是無效的。也就是說,p 的長度不在 \(0\)\(4N\) 之間(含 \(0\)\(4N\)),或者 p 的某些字符不是 ABXY
  • too many moves:函數 press 的調用次數超過 \(8\ 000\) 次。
  • wrong guessguess_sequence 返回的不是 \(S\)

題解

首先搞定第一個字母。先查詢 ABXY,然後就知道首字母是哪兩個中的一個,再查詢一次就好了。使用次數 \(2\)
然後考慮我們假設確定的字符串為 S 怎麽知道下一位。我們可以考慮讓剩下三個字母在詢問中通過增加不同的貢獻表現出來。這樣構造,S+p[1]+S+p[2]+p[1]+S+p[2]+p[2]+S+p[2]+p[3],其中p[i]是除了首字母外的其它字母。這樣構造的好處就是,如果下一位是p[1],那麽會多 \(1\) 的貢獻,如果是p[2],會多 \(2\) 的貢獻,否則多出的貢獻為 \(0\) 。這樣就可以把每一位給問出來。但是最後一位因為長度限制,問不了那麽長的字符串,那麽就只問到 \(n-1\) 位就好了。使用次數 \(n-2\)
最後一位采取與第一位相同的方法問出來就好了。使用次數 \(2\)
最後總共使用次數 \(2+n-2+2=n+2\) 次,正好

#include<bits/stdc++.h>
#include "combo.h"
using namespace std;

string guess_sequence(int n)
{
    string s="",p[4];
    int coins=0;
    coins=press("AB");
    if(coins)s=press("A")?"A":"B";
    else s=press("X")?"X":"Y";
    if(s=="A")p[1]="B",p[2]="X",p[3]="Y";
    if(s=="B")p[1]="A",p[2]="X",p[3]="Y";
    if(s=="X")p[1]="A",p[2]="B",p[3]="Y";
    if(s=="Y")p[1]="A",p[2]="B",p[3]="X";
    if(n==1)return s;
    for(register int i=2;i<=n-1;++i)
    {
        coins=press(s+p[1]+s+p[2]+p[1]+s+p[2]+p[2]+s+p[2]+p[3]);
        if(coins==i)s=s+p[1];
        else if(coins==i-1)s=s+p[3];
        else s=s+p[2];
    }
    coins=press(s+p[1]);
    if(coins==n)s=s+p[1];
    else
    {
        coins=press(s+p[2]);
        if(coins==n)s=s+p[2];
        else s=s+p[3];
    }
    return s;
}

【刷題】LOJ 2863 「IOI2018」組合動作