結果
問題 | No.5002 stick xor |
ユーザー |
|
提出日時 | 2025-04-27 03:11:53 |
言語 | PyPy3 (7.3.15) |
結果 |
TLE
|
実行時間 | - |
コード長 | 11,305 bytes |
コンパイル時間 | 525 ms |
コンパイル使用メモリ | 82,504 KB |
実行使用メモリ | 88,172 KB |
スコア | 13,648 |
最終ジャッジ日時 | 2025-04-27 03:12:46 |
合計ジャッジ時間 | 35,901 ms |
ジャッジサーバーID (参考情報) |
judge1 / judge5 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
other | AC * 10 TLE * 22 |
ソースコード
import copy import random from time import perf_counter import argparse import sys import math random.seed(0) class TimeKeeper: def __init__(self): self.start_time = perf_counter() def is_time_over(self, LIMIT): return (perf_counter() - self.start_time) >= LIMIT def time_now(self): return (perf_counter() - self.start_time) class Solver: def __init__(self, N, K, L, W, A): self.N = N # グリッドサイズ self.K = K # 操作回数 self.L = L # 長方形サイズリスト self.W = W # 初期の白(0)の数 self.A0 = [row[:] for row in A] def initialize(self): # Aをリセット self.A = [row[:] for row in self.A0] def place(self, pos): a, b, c, d = pos for i in range(a, c+1): for j in range(b, d+1): self.A[i][j] = 1 - self.A[i][j] def calscore(self, ans): # ansでひっくり返した後の白(0)数を計算 A_ = [row[:] for row in self.A0] for a, b, c, d in ans: for i in range(a, c+1): for j in range(b, d+1): A_[i][j] = 1 - A_[i][j] return sum((1 - A_[i][j]) for i in range(self.N) for j in range(self.N)) - self.W def _greedy_initial(self, order): # order は操作インデックスのリスト self.initialize() ans = [None] * self.K B1 = 2 B2 = 5 for idx in order: D = self.L[idx] best_pos = (0, 0, 0, D-1) mxcnt = -1 # 横向きスキャン for i in range(self.N): now = sum(self.A[i][j] for j in range(D)) bonus = B1 + (B1 if self.A[i][1] == 0 else 0) + (B2 if self.A[i][0] == 1 else 0) + (B2 if self.A[i][D-1] == 1 else 0) if now + bonus > mxcnt: mxcnt = now + bonus best_pos = (i, 0, i, D-1) for j in range(self.N - D): now += self.A[i][j+D] - self.A[i][j] bonus = (B1 if self.A[i][j] == 0 else 0) + (B1 if (j+D+1 >= self.N or self.A[i][j+D+1] == 0) else 0) bonus += (B2 if self.A[i][j+1] == 1 else 0) + (B2 if self.A[i][j+D] == 1 else 0) if now + bonus > mxcnt: mxcnt = now + bonus best_pos = (i, j+1, i, j+D) # 縦向きスキャン for j in range(self.N): now = sum(self.A[i][j] for i in range(D)) bonus = B1 + (B1 if self.A[1][j] == 0 else 0) + (B2 if self.A[0][j] == 1 else 0) + (B2 if self.A[D-1][j] == 1 else 0) if now + bonus > mxcnt: mxcnt = now + bonus best_pos = (0, j, D-1, j) for i in range(self.N - D): now += self.A[i+D][j] - self.A[i][j] bonus = (B1 if self.A[i][j] == 0 else 0) + (B1 if (i+D+1 >= self.N or self.A[i+D+1][j] == 0) else 0) bonus += (B2 if self.A[i+1][j] == 1 else 0) + (B2 if self.A[i+D][j] == 1 else 0) if now + bonus > mxcnt: mxcnt = now + bonus best_pos = (i+1, j, i+D, j) # 選んだbest_posを配置 self.place(best_pos) ans[idx] = best_pos return ans def solve(self, tk, LIMIT): # 初期解を複数パターンで生成 patterns = [] # サイズ大きい順 patterns.append(sorted(range(self.K), key=lambda k: self.L[k], reverse=True)) # ランダム順 (異なるシードで) for seed in range(2): rnd = random.Random(seed) order = list(range(self.K)) rnd.shuffle(order) patterns.append(order) best_ans = None best_sc = -float('inf') # 各パターンでグリーディ生成 for order in patterns: ans0 = self._greedy_initial(order) sc0 = self.calscore(ans0) if sc0 > best_sc: best_sc = sc0 best_ans = ans0 # best_ans を現在の解としてセット ans = best_ans[:] curr_sc = best_sc best_overall = ans[:] best_overall_sc = best_sc # self.A を best_ans 適用後の状態にリセット self.initialize() for pos in ans: self.place(pos) # 焼きなまし法パラメータ T0, T1 = 0.5, 0.01 loop = 0 # 焼きなまし繰り返し # 長方形の位置をずらす loop = 0 while True: if tk.is_time_over(LIMIT): break loop += 1 # 温度 t = tk.time_now() progress = t / LIMIT temp = T0 + (T1 - T0) * progress # Aの値を減らしていく rnd = random.randrange(100) if rnd < 99: n = random.randrange(self.K) a0, b0, c0, d0 = ans[n] kouho = [] if a0 == c0: # 横長 if d0 != self.N-1 and random.randrange(2) == 0: # 右にずらす dsc = (self.A[a0][b0] + self.A[c0][d0+1] - 1) * 2 kouho.append((dsc, "R")) elif b0 != 0 and random.randrange(2) == 1: # 左にずらす dsc = (self.A[a0][b0-1] + self.A[c0][d0] - 1) * 2 kouho.append((dsc, "L")) elif b0 == d0: # 縦長 if c0 != self.N-1 and random.randrange(2) == 0: # 下にずらす dsc = (self.A[a0][b0] + self.A[c0+1][d0] - 1) * 2 kouho.append((dsc, "D")) elif a0 != 0 and random.randrange(2) == 1: # 上にずらす dsc = (self.A[a0-1][b0] + self.A[c0][d0] - 1) * 2 kouho.append((dsc, "U")) if len(kouho) == 0: continue dsc, direction = kouho[0] if dsc >= 0: # if dsc >= 0 or random.random() < math.exp(dsc / temp): # 採用 curr_sc += dsc if direction == "R": self.place((a0, b0, c0, d0)) self.place((a0, b0+1, c0, d0+1)) ans[n] = (a0, b0+1, c0, d0+1) elif direction == "L": self.place((a0, b0, c0, d0)) self.place((a0, b0-1, c0, d0-1)) ans[n] = (a0, b0-1, c0, d0-1) elif direction == "D": self.place((a0, b0, c0, d0)) self.place((a0+1, b0, c0+1, d0)) ans[n] = (a0+1, b0, c0+1, d0) elif direction == "U": self.place((a0, b0, c0, d0)) self.place((a0-1, b0, c0-1, d0)) ans[n] = (a0-1, b0, c0-1, d0) if curr_sc > best_sc: print(f"best {loop=}", file=sys.stderr) best_sc = curr_sc best_ans = ans[:] else: n = random.randrange(self.K) a0, b0, c0, d0 = ans[n] D = max(abs(a0 - c0), abs(b0 - d0))+1 ones0 = sum(self.A[i][j] for i in range(a0,c0+1) for j in range(b0,d0+1)) zeros0 = D - ones0 dsc0 = ones0 - zeros0 # 一旦消去 self.place((a0, b0, c0, d0)) mxcnt = -1 best_pos = (0, 0, 0, D-1) B1 = 0 B2 = 0 # 横向きスキャン for i in range(self.N): now = sum(self.A[i][j] for j in range(D)) bonus = B1 + (B1 if self.A[i][1] == 0 else 0) + (B2 if self.A[i][0] == 1 else 0) + (B2 if self.A[i][D-1] == 1 else 0) if now + bonus > mxcnt: mxcnt = now + bonus best_pos = (i, 0, i, D-1) for j in range(self.N - D): now += self.A[i][j+D] - self.A[i][j] bonus = (B1 if self.A[i][j] == 0 else 0) + (B1 if (j+D+1 >= self.N or self.A[i][j+D+1] == 0) else 0) bonus += (B2 if self.A[i][j+1] == 1 else 0) + (B2 if self.A[i][j+D] == 1 else 0) if now + bonus > mxcnt: mxcnt = now + bonus best_pos = (i, j+1, i, j+D) # 縦向きスキャン for j in range(self.N): now = sum(self.A[i][j] for i in range(D)) bonus = B1 + (B1 if self.A[1][j] == 0 else 0) + (B2 if self.A[0][j] == 1 else 0) + (B2 if self.A[D-1][j] == 1 else 0) if now + bonus > mxcnt: mxcnt = now + bonus best_pos = (0, j, D-1, j) for i in range(self.N - D): now += self.A[i+D][j] - self.A[i][j] bonus = (B1 if self.A[i][j] == 0 else 0) + (B1 if (i+D+1 >= self.N or self.A[i+D+1][j] == 0) else 0) bonus += (B2 if self.A[i+1][j] == 1 else 0) + (B2 if self.A[i+D][j] == 1 else 0) if now + bonus > mxcnt: mxcnt = now + bonus best_pos = (i+1, j, i+D, j) # 選んだbest_posを配置 self.place(best_pos) ans[n] = best_pos ones1 = sum(self.A[i][j] for i in range(best_pos[0],best_pos[2]+1) for j in range(best_pos[1],best_pos[3]+1)) zeros1 = D - ones1 dsc1 = ones1 - zeros1 curr_sc += dsc0 - dsc1 if curr_sc > best_sc: print(f"best2 {loop=}", file=sys.stderr) best_sc = curr_sc best_ans = ans[:] # print(ans, file=sys.stderr) # sc = self.calscore(ans) return best_ans, best_sc ########################################### def main(DEBUG): tk = TimeKeeper() LIMIT = 0.8 N, K = map(int, input().split()) L = list(map(int, input().split())) S = [list(input().rstrip()) for _ in range(N)] W = 0 A = [[0]*N for _ in range(N)] for i in range(N): for j in range(N): A[i][j] = int(S[i][j]) W += (1 - A[i][j]) # N = 60 グリッドサイズ # K = 500 操作回数 # 1 <= Li <= 25 長方形サイズ # W 初期Aの白(0)の総数 solver = Solver(N, K, L, W, A) ans, sc = solver.solve(tk, LIMIT) for i in range(K): ANS = [x+1 for x in ans[i]] print(*ANS) print("SC", sc, file=sys.stderr) return if __name__ == '__main__': parser = argparse.ArgumentParser(description='Debug mode') parser.add_argument('--debug', action='store_true', help='Enable debug mode') args = parser.parse_args() main(args.debug)