P3444 [POI2006]ORK-Ploughing
*P3444 [POI2006]ORK-Ploughing
POI 合集。
有趣的思维题。
考虑到最后必然是行或者列被删完,所以我们不妨设行被删完。
接下来,考虑固定左右各删去多少列,即设
区间
考虑如何求出
同理,假设列被删完再做一遍上述算法即可得到正确答案。
const int N = 2e3 + 5;
int n, m, k, ans = N << 1, row[N][N], c[N][N];
pii f[N][N];
int main(){
cin >> k >> m >> n;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
c[i][j] = c[i - 1][j] + (row[i][j] = read()), row[i][j] += row[i][j - 1];
for(int i = 1; i <= n; i++)
for(int j = i; j <= n; j++)
f[i][j] = {0, m}; f[1][n] = {1, m};
for(int len = n; len; len--)
for(int l = 1, r = len; r <= n; l++, r++) {
int p = f[l][r].fi, q = f[l][r].se;
if(!p) continue;
while(p <= q && c[r][p] - c[l - 1][p] <= k) p++;
while(p <= q && c[r][q] - c[l - 1][q] <= k) q--;
if(p > q) cmin(ans, n - len + m);
if(row[l][q] - row[l][p - 1] <= k) f[l + 1][r] = {p, q};
if(row[r][q] - row[r][p - 1] <= k) f[l][r - 1] = {p, q};
}
for(int i = 1; i <= m; i++)
for(int j = i; j <= m; j++)
f[i][j] = {0, n}; f[1][m] = {1, n};
for(int len = m; len; len--)
for(int l = 1, r = len; r <= m; l++, r++) {
int p = f[l][r].fi, q = f[l][r].se;
if(!p) continue;
while(p <= q && row[p][r] - row[p][l - 1] <= k) p++;
while(p <= q && row[q][r] - row[q][l - 1] <= k) q--;
if(p > q) cmin(ans, n + m - len);
if(c[q][l] - c[p - 1][l] <= k) f[l + 1][r] = {p, q};
if(c[q][r] - c[p - 1][r] <= k) f[l][r - 1] = {p, q};
}
cout << ans << endl;
return 0;
}