Loading

Harvester 题解

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;
}
posted @ 2023-11-06 17:59  TKXZ133  阅读(72)  评论(0)    收藏  举报