【刷題】LOJ 2863 「IOI2018」組合動作
題目描述
你在玩一個動作遊戲。遊戲控制器有 \(4\) 個按鍵,A
、B
、X
和 Y
。在遊戲中,你用組合動作來賺金幣。你可以依次按這些按鍵來完成一個組合動作。 這個遊戲有一個隱藏的按鍵序列,可以表示為由這 \(4\) 個字符組成的串 \(S\) 。你並不知道這個串 \(S\) ,但是你知道它的長度為 \(N\) 。
你還知道,\(S\) 的首字符不會在串中重復出現。 例如,\(S\) 可以是“ABXYY
”或者“XYYAA
”,但不能是“AAAAA
”或“BXYBX
”。
你可以依次按最多 \(4N\) 個按鍵來完成一個組合動作。串 \(p\) 為你所按的按鍵序列。你用這個組合動作賺到的金幣數量,等於同時為 \(p\)
例如,如果 \(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
的每個字符必須是A
、B
、X
或者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\) 的每個字符必須是
A
、B
、X
或Y
。 - \(S\) 的首字符不會再 \(S\) 中重復出現。
在本題中,評測程序不是適應性的。意思是說,在評測程序開始運行的時候 \(S\) 就固定下來,而且不依賴於你的程序所做的詢問。
子任務
- (5分)\(N=3\)
- (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
的某些字符不是A
、B
、X
和Y
。too many moves
:函數press
的調用次數超過 \(8\ 000\) 次。wrong guess
:guess_sequence
返回的不是 \(S\)。
題解
首先搞定第一個字母。先查詢 AB
或XY
,然後就知道首字母是哪兩個中的一個,再查詢一次就好了。使用次數 \(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」組合動作