/* 答えで二分探索 dpdiff[i][j] : 位置 i まで見て,直前の種類が j で,次は j 以外と決めたときの,ペナルティが発生しない回数の最小値 dpsame[i][j] : 位置 i まで見て,直前の種類が j で,次も j と決めたときの,ペナルティが発生しない回数の最小値 next dp で次元を減らす dpdiff は top2 しか持たなくて良い */ #include using namespace std; const int iinf = 1001001007; bool chmin(auto &a, auto b){ return a > b ? a = b, 1 : 0; } bool chmax(auto &a, auto b){ return a < b ? a = b, 1 : 0; } struct top2 { int v1, i1; int v2, i2; top2 () : v1(iinf), i1(-1), v2(iinf), i2(-1) {} void upd(int v, int i){ if (v < v1){ v2 = v1; i2 = i1; v1 = v; i1 = i; } else if (v < v2){ v2 = v; i2 = i; } } int get(int except){ return (i1 == except ? v2 : v1); } }; int main(){ cin.tie(0)->sync_with_stdio(0); int n, m, c, x; cin >> n >> m >> c >> x; vector a(n,vector(m)); for (int i = 0; i < n; i++){ for (int j = 0; j < m; j++){ cin >> a[i][j]; } } int le = -iinf, ri = iinf; while (ri - le > 1){ int md = midpoint(le, ri); top2 dpdiff; vector dpsame(m,iinf); dpdiff.upd(0,-1); for (int i = 0; i < n; i++){ top2 ndpdiff; vector ndpsame(m,iinf); for (int j = 0; j < m; j++){ // diff if (a[i][j] >= md){ ndpdiff.upd(dpdiff.get(j)+1,j); } // same if (a[i][j] >= md+c){ ndpdiff.upd(dpsame[j],j); chmin(ndpsame[j],dpsame[j]); chmin(ndpsame[j],dpdiff.get(j)); } } swap(ndpdiff,dpdiff); swap(ndpsame,dpsame); } if (dpdiff.v1 <= n-x){ le = md; } else { ri = md; } } cout << le << endl; }