Harvester 题解
题目大意
给定 \(n\times m\) 的网格,每次可以选一行或一列,将这一行或一列上的数全部取走,最多可以取四次,问取走的数的和的最大值。
思路分析
没事干了把以前写过的题拿出来写题解。
分类讨论题。
在只能取四次的情况下一共只有这么几种可能:
- 选四行:
毫无疑问,行之间互不干扰,把每行的和求出来选四个最大的就可以了。
-
选四列: 类似于选四行的情况。
-
选一行三列:
枚举选哪行,再扫一遍求出去掉这一行之后最大的三列就行了,时间复杂度 \(O(nm)\)。(不过用排序求的时间复杂度就是 \(O(nm\log m)\))
-
选一列三行:类似于选一行三列的情况。
-
选两行两列:
枚举选哪两行,再扫一遍求出去掉这两行之后最大的两列就行了。时间复杂度 \(O(n^2m)\)。
注意到 \(n\) 很大,\(m\) 很小时时间复杂度会炸,因此这时我们直接将行列交换就行了。时间复杂度为 \(O(\min(n^2m, m^2n))=O(nm\sqrt {nm})\)。
将所有情况取最大值就做完了,总时间复杂度为 \(O(nm\sqrt {nm}+nm\log (n+m))\)。
代码
(写麻烦了)
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
using namespace std;
const int N = 100100;
#define inf 0x3f3f3f3f
#define int long long
int n, m, ans;
int mp[N], sum13[N], sum31[N], sum42[N], sum24[N];
int point(int x, int y){
if (x < 1 || y < 1 || x > n || y > m) return 0;
return (x - 1) * m + y;
}
bool cmp(int a, int b){
return a > b;
}
int calc(vector <int> v, int siz){
int res = 0;
for (int i = 0; i < min(siz, (int)v.size()); i ++) res += v[i];
return res;
}
signed main(){
scanf("%lld %lld", &n, &m);
for (int i = 1; i <= n * m; i ++)
scanf("%lld", &mp[i]);
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++) {
sum42[point(i, j)] = sum42[point(i, j - 1)] + mp[point(i, j)];
sum13[point(i, j)] = sum13[point(i - 1, j)] + mp[point(i, j)];
}
for (int i = n; i >= 1; i --)
for (int j = m; j >= 1; j --) {
sum24[point(i, j)] = sum24[point(i, j + 1)] + mp[point(i, j)];
sum31[point(i, j)] = sum31[point(i + 1, j)] + mp[point(i, j)];
}
vector <int> v1, v2;
for (int i = 1; i <= n; i ++) v1.push_back(sum24[point(i, 1)]);
for (int i = 1; i <= m; i ++) v2.push_back(sum31[point(1, i)]);
sort(v1.begin(), v1.end(), cmp);
sort(v2.begin(), v2.end(), cmp);
ans = max({ans, calc(v1, 4), calc(v2, 4)});
for (int i = 1; i <= n; i ++) {
vector <int> v;
for (int j = 1; j <= m; j ++)
v.push_back(sum13[point(i - 1, j)] + sum31[point(i + 1, j)]);
sort(v.begin(), v.end(), cmp);
ans = max(ans, sum24[point(i, 1)] + calc(v, 3));
}
for (int i = 1; i <= m; i ++) {
vector <int> v;
for (int j = 1; j <= n; j ++)
v.push_back(sum42[point(j, i - 1)] + sum24[point(j, i + 1)]);
sort(v.begin(), v.end(), cmp);
ans = max(ans, sum31[point(1, i)] + calc(v, 3));
}
if (n < m) {
for (int i = 1; i <= n; i ++)
for (int j = i + 1; j <= n; j ++) {
int mmax = 0, smax = 0;
for (int k = 1; k <= m; k ++) {
int t = sum31[point(1, k)] - mp[point(i, k)] - mp[point(j, k)];
if (t >= mmax) smax = mmax, mmax = t;
else if (t >= smax) smax = t;
}
ans = max(ans, sum24[point(i, 1)] + sum24[point(j, 1)] + mmax + smax);
}
}
else {
for (int i = 1; i <= m; i ++)
for (int j = i + 1; j <= m; j ++) {
int mmax = 0, smax = 0;
for (int k = 1; k <= n; k ++) {
int t = sum24[point(k, 1)] - mp[point(k, i)] - mp[point(k, j)];
if (t >= mmax) smax = mmax, mmax = t;
else if (t >= smax) smax = t;
}
ans = max(ans, sum31[point(1, i)] + sum31[point(1, j)] + mmax + smax);
}
}
cout << ans << '\n';
return 0;
}

浙公网安备 33010602011771号