import sys def solve(): # 入力の読み込み try: N, M, X, C = map(int, sys.stdin.readline().split()) A = [list(map(int, sys.stdin.readline().split())) for _ in range(N)] except ValueError: print("入力エラー: N, M, X, C または A の形式が正しくありません。") return # ----------------------------------------------------------------- # 判定関数: 満足度の最小値を K 以上にできるか? # ----------------------------------------------------------------- def is_possible(K): # --- N=1 (エッジケース) --- if N == 1: if X > 0: return False # ペナルティは発生し得ない max_score = -1 for j in range(M): max_score = max(max_score, A[0][j]) return max_score >= K # --- N >= 2 (DP) --- # DPテーブル: dp_curr[j][k] # 位置 i-1 がカテゴリ j, 位置 i がカテゴリ k のときの、 # 位置 1 から i-1 までのペナルティ発生位置の最大総数 # 到達不能は -1 で初期化 dp_curr = [[-1] * M for _ in range(M)] # --- 初期化 (i=2) --- # j = cat[1], k = cat[2] for j in range(M): for k in range(M): is_penalized_1 = (j == k) score_1 = A[0][j] - (C if is_penalized_1 else 0) if score_1 >= K: dp_curr[j][k] = 1 if is_penalized_1 else 0 # --- DP遷移 (i = 3 to N) --- for i in range(3, N + 1): dp_prev = dp_curr dp_curr = [[-1] * M for _ in range(M)] # i-1 のスコアインデックス (A[score_idx]) score_idx = i - 2 # DP遷移を高速化するための前計算 O(M^2) # precalc_no_penalty[j]: # cat[i-2] != j かつ score[i-1] >= K を満たす j_prev からの # max(dp_prev[j_prev][j] + 0) # precalc_with_penalty[j]: # cat[i-2] == j かつ score[i-1] >= K を満たす j_prev からの # max(dp_prev[j_prev][j] + 1) precalc_no_penalty = [-1] * M precalc_with_penalty = [-1] * M for j in range(M): # cat[i-1] val_no_penalty = A[score_idx][j] val_with_penalty = A[score_idx][j] - C max_A = -1 # (j_prev != j) max_B = -1 # (j_prev == j) for j_prev in range(M): # cat[i-2] prev_count = dp_prev[j_prev][j] if prev_count == -1: continue if j == j_prev: if val_with_penalty >= K: max_B = max(max_B, prev_count + 1) else: if val_no_penalty >= K: max_A = max(max_A, prev_count + 0) precalc_no_penalty[j] = max_A precalc_with_penalty[j] = max_B # dp_curr[j][k] の計算 O(M^2) for j in range(M): # cat[i-1] for k in range(M): # cat[i] res = -1 # (j == k) の場合 (位置 i-1 はペナルティ確定) if j == k: # (j_prev == j) からの遷移 res = max(res, precalc_with_penalty[j]) # (j_prev != j) からの遷移 # この場合、i-1 は (j==k) のせいでペナルティを受ける if A[score_idx][j] - C >= K: if precalc_no_penalty[j] != -1: # (dp_prev[j_prev][j] + 0) + 1 res = max(res, precalc_no_penalty[j] + 1) # (j != k) の場合 else: # (j_prev == j) からの遷移 (i-1 はペナルティ確定) res = max(res, precalc_with_penalty[j]) # (j_prev != j) からの遷移 (i-1 はペナルティなし) res = max(res, precalc_no_penalty[j]) dp_curr[j][k] = res # --- 最終確認 (i = N の後) --- # dp_curr[j][k] には 1...N-1 までのペナルティ最大数が入っている # 最後に位置 N のスコアとペナルティを考慮する max_total_penalty = -1 for j in range(M): # cat[N-1] for k in range(M): # cat[N] penalties_1_to_N_minus_1 = dp_curr[j][k] if penalties_1_to_N_minus_1 == -1: continue is_penalized_N = (j == k) score_N = A[N - 1][k] - (C if is_penalized_N else 0) if score_N >= K: penalty_N = 1 if is_penalized_N else 0 total = penalties_1_to_N_minus_1 + penalty_N max_total_penalty = max(max_total_penalty, total) return max_total_penalty >= X # ----------------------------------------------------------------- # 二分探索の実行 # ----------------------------------------------------------------- # L: 達成可能, R: 達成不可能 # 答えの範囲 [0, max(A)+1] L = 0 R = 10**15 + 7 # 十分大きな値 (Aijの最大値 + 1) while R - L > 1: mid = (L + R) // 2 if is_possible(mid): L = mid else: R = mid print(L) # スクリプトとして実行 if __name__ == "__main__": solve()