結果
| 問題 |
No.3018 目隠し宝探し
|
| コンテスト | |
| ユーザー |
navel_tos
|
| 提出日時 | 2025-01-25 17:59:23 |
| 言語 | PyPy3 (7.3.15) |
| 結果 |
AC
|
| 実行時間 | 142 ms / 2,000 ms |
| コード長 | 9,430 bytes |
| コンパイル時間 | 277 ms |
| コンパイル使用メモリ | 82,816 KB |
| 実行使用メモリ | 78,976 KB |
| 平均クエリ数 | 2.64 |
| 最終ジャッジ日時 | 2025-01-25 23:59:38 |
| 合計ジャッジ時間 | 4,451 ms |
|
ジャッジサーバーID (参考情報) |
judge6 / judge1 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 1 |
| other | AC * 21 |
ソースコード
#インタラクティブ デバッグセット
'''
グローバルのinput・printを上から書き換え、ジャッジ側をyieldで律速する仕組みです。
■使い方
1. 書式通りに答案プログラム・ジャッジプログラムを用意してください。
- 答案: 普段通りinput()とprint()を使ってください exit()があれば書き換えてください
- ジャッジ: 入力受取はinput()、答案への出力はyield、WAの判定はassertを使ってください
2. テストケースを渡し実行してください。WAが出た場合エラーを出して停止します。
3. テストケースは testcase に、直近のin-outログは judge_log に格納されています。
printデバッグを行う場合、 pprint() とすると従来のprintと同様の関数が扱えます。
'''
#内部関数
pprint, _judge_text, _nonflush_text, judge_log = print, [], [], []
def _solve_input(): #答案 ← ジャッジ 主な律速部分
global input, print, _judge_generator
input = _judge_input #ジャッジ実行前の調整
_judge_text.reverse()
if type( txt := next( _judge_generator ) ) != str:
if type(txt) == list or type(txt) == tuple:
txt = ' '.join([ str(Ti) for Ti in txt ])
else:
txt = str(txt)
input = _solve_input #答案実行用に修正
assert len( _judge_text ) == 0, 'Too many questions printed.'
judge_log.append( f'{txt} <judge>' )
return txt
def _solve_print(*value, sep = ' ', end = '\n', file = None, flush = False):
_nonflush_text.append( sep.join([ str(Vi) for Vi in value ]) )
if end == '\n': #ここまでの文字列を結合し、judge_textに正式に送る
_judge_text.append( txt := ''.join( _nonflush_text ) )
judge_log.append( txt )
_nonflush_text.clear()
else:
_nonflush_text.append( end )
def _judge_input(): #答案 → ジャッジ
assert len( _judge_text ) > 0, 'No output from solver_program.'
return _judge_text.pop()
def _control_panel( solve_program, judge_program, current_testcase ): #1ケース実行
global input, print, _judge_generator, testcase
_judge_text.clear(); _nonflush_text.clear(); judge_log.clear()
testcase = current_testcase
_judge_generator = judge_program( * current_testcase )
input, print = _solve_input, _solve_print
solve_program()
input = _judge_input
try:
txt = next( _judge_generator )
assert False, f'judge program did not stop properly: {txt = }'
except StopIteration:
return
def _test_one( solve_program, judge_program, current_testcase ):
global input, print
try:
_control_panel( solve_program, judge_program, current_testcase )
input = print = 1; del input, print
except Exception as exp: #エラーの出現を留保して終了処理
input = print = 1; del input, print
print('Wrong Answer.')
raise exp
def _test_mul( solve_program, judge_program, make_testcase_program, test_time ):
global input, print
for t in range(1, test_time + 1):
case = make_testcase_program()
try:
_control_panel( solve_program, judge_program, case )
except Exception as exp:
input = print = 1; del input, print
print('Wrong Answer in testcase', t)
raise exp
else:
input = print = 1; del input, print
print('Accepted all testcases.')
#実行テスト1. 1ケース実行
def test1( solve_program, judge_program, current_testcase: list ):
'''
testcaseにはjudge_programの引数指定順に値を入力してください。
judge( N, K, A )の場合、test( solve, judge, [N, K, A] ) のようにしてください。
'''
_test_one( solve_program, judge_program, current_testcase )
#実行テスト2: ランダムテスト
def test2( solve_program, judge_program, make_testcase_program, test_time = 1000 ):
'''
テストケース生成器を基にランダムテストを行います。
生成器の出力順は、judge_programの引数指定順にしてください。
'''
_test_mul( solve_program, judge_program, make_testcase_program, test_time )
#ジャッジ入出力ログを展開
def open_log():
print( * judge_log, sep = '\n' )
'''
ここからジャッジプログラムを作成してください。
答案 → ジャッジ の入力には print() を、
答案 ← ジャッジ の出力には yield を使ってください。
従来の組み込み関数のprintでデバッグする場合、printの代わりにpprintと入力してください。
'''
#verified with:
#1. ABC299D Find by Query
#https://atcoder.jp/contests/abc299/submissions/61996827
#2. ABC305F Dungeon Explore
#https://atcoder.jp/contests/abc305/tasks/abc305_f
#3. ABC313D Odd or Even
#https://atcoder.jp/contests/abc313/tasks/abc313_d
#4. practice contest B Interactive Sorting
#https://atcoder.jp/contests/practice/submissions/62002330
#5. yukicoder3018 目隠し宝探し
#https://yukicoder.me/problems/no/3018
from random import randint
def make_test( maxH = 6, maxW = 6 ):
h = randint( 1, H := randint(1, maxH) )
w = randint( 1, W := randint(1, maxW) )
return H, W, h, w
#H, Wを与える。宝(h, w)の位置を問わず答えを導ける最小の質問回数を返す
memo_P = dict()
def calc_P(H, W):
if (H, W) in memo_P:
return memo_P[ (H, W) ]
#ジャッジでは1-indexedだが、ここでは0-indexedで扱う
#P: 質問履歴をソートしたもの
#DP[P]: P個の質問を投げた状態から、「どこに宝があったとしても解が一意に定まる状態」に
# なるまでの最小の質問回数
#・・・ と定義し、メモ化再帰を行う。状態数は 2 ** HW
DP = dict()
stack = [(0, tuple())]
while stack:
flag, P = stack.pop()
if flag == 0: #行きがけの処理
if P in DP: #既に解が出ているなら
continue
#sub[h][w]: 真の宝が(h, w)にあると仮定したとき、すべての質問履歴と矛盾ない
# マスの個数。特に、(h, w)は矛盾がないので1以上を取る
sub = [[0] * W for _ in range(H)]
for h in range(H):
for w in range(W):
for x in range(H): #暫定のマス
for y in range(W):
if all( (h - i) ** 2 + (w - j) ** 2 ==
(x - i) ** 2 + (y - j) ** 2 for i, j in P ):
sub[h][w] += 1
if all( sub[h][w] == 1 for h in range(H) for w in range(W) ):
DP[P] = 0 #どこに宝があっても一意に定まる状態
continue
#次の質問を飛ばす
stack.append((~ 0, P))
for i in range(H):
for j in range(W):
if (i, j) in P: #質問済みなら
continue
nP = tuple( sorted(list(P) + [(i, j)]) )
if nP not in DP:
stack.append((0, nP))
else: #返りがけの処理
#DP[P] = min( DP[次の状態] ) + 1 とすればよい
cnt = 10 ** 18
for i in range(H):
for j in range(W):
if (i, j) in P: continue
nP = tuple( sorted(list(P) + [(i, j)]) )
cnt = min(cnt, DP[nP])
DP[P] = cnt + 1
memo_P[ (H, W) ] = DP[ tuple() ]
return DP[ tuple() ]
#上記のP計算を用いてジャッジを行う
def judge(H, W, h, w):
P = calc_P(H, W)
Q = []
yield H, W
for q in range(P + 1):
t, i, j = input().split()
assert 1 <= ( i := int(i) ) <= H and 1 <= ( j := int(j) ) <= W, 'out of grid'
if t == '!':
break
assert t == '?'
assert q < P, 'question over P times.'
yield (i - h) ** 2 + (j - w) ** 2
Q.append((i, j)) #質問履歴
#以上の質問で一意に定まっているか判定
assert t == '!'
candidate = []
for x in range(1, H + 1):
for y in range(1, W + 1):
if all((i - h) ** 2 + (j - w) ** 2 == (i - x) ** 2 + (j - y) ** 2
for i, j in Q):
candidate.append((x, y))
assert len(candidate) == 1, f'left other candidates: {candidate =}'
return
#答案作成
def solve():
H, W = map(int, input().split())
if H == W == 1:
print('!', 1, 1)
return
print('?', 1, 1)
c1 = int(input())
if c1 == 0:
print('!', 1, 1)
return
if H == 1:
for w in range(1, W + 1):
if (1 - w) ** 2 == c1:
print('!', 1, w)
return
if W == 1:
for h in range(1, H + 1):
if (1 - h) ** 2 == c1:
print('!', h, 1)
return
print('?', 1, 2)
c2 = int(input())
for h in range(1, H + 1):
for w in range(1, W + 1):
if (1 - h) ** 2 + (1 - w) ** 2 == c1 and\
(1 - h) ** 2 + (2 - w) ** 2 == c2:
print('!', h, w)
return
#テストを実行
#test2(solve, judge, make_test, 10000)
#大丈夫そうなので提出
solve()
navel_tos