网易2017校园招聘数据挖掘笔试题编程题 分田地

题目:

牛牛和15个朋友玩打土豪分田地的游戏,牛牛决定让你来分田地,地主的田地可以看成是一个矩形,每个位置有一个价值。分割田地的方法是横竖各切三刀,分成16份,作为领导干部,牛牛总是会选择其中总价值最小的一份田地,作为牛牛最好的朋友,你希望牛牛取得的田地的价值和尽可能大,你知道这个值最大可以是多少吗?

输入描述

每个输入包含1个测试用例。每个测试用例的第一行包含两个整数n和m(1 <= n,m <= 75),表示田地的大小,接下来的n行,每行包含m个0-9之间的数字,表示每块位置的价值。

输出描述

输出一行表示牛牛所能取得的最大的价值。

输入例子

4 4
3332
3233
3332
2323

输出例子

2

思路

“最小值最大问题”,先二分试试。
枚举横向的三刀,纵向上在二分答案后贪心地靠左切,使得每个区域刚好全部大于等于答案,根据能否切满三刀来确定答案区间。这个时间复杂度算一下是 O(n3mlogx) 的,其中x是最大可能答案。

对于题目中给的数据,这样的复杂度表示鸭梨山大(codeforces.com除外),得考虑一下如何优化。
依然枚举横向的三刀,现在问题是如何确定这三刀,使得16个区域最小值最大。不妨先切中间那刀,那么问题变为如何切左边那刀和右边那刀,很显然这两个问题相互独立。用f[i]表示前i列的最优切法所得到的值,g[i]表示i~m列的最优切法得到的值,那么答案 =max(min(f[i],g[i+1])),1<=i<m 。容易证明f[i]和g[i]的最优决策都是单调的,于是它们都可以在 O(m) 的时间内算出来,因此最后复杂度变为 O(n3m)

参考程序一(枚举+二分):
#include <bits/stdc++.h>
using namespace std;
int n, m, a[100][100], sum[100][100], l, r, M;

int getsum(int x1, int y1, int x2, int y2) {
    return sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 - 1] + sum[x1 - 1][y1 - 1];
}
bool ok(int val, int ix, int jx, int kx, int y1, int y2) {
    int v1 = getsum(1, y1, ix, y2);
    int v2 = getsum(ix + 1, y1, jx, y2);
    int v3 = getsum(jx + 1, y1, kx, y2);
    int v4 = getsum(kx + 1, y1, n, y2);
    return v1 >= val && v2 >= val && v3 >= val && v4 >= val;
}
bool chk(int x) {
    int c, y1;
    for (int i = 1; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
            for (int k = j + 1; k < n; k++) {
                c = 0;
                y1 = 1;
                for (int y2 = 1; y2 <= m && c < 4; y2++) {
                    if (ok(x, i, j, k, y1, y2)) {
                        c++;
                        y1 = y2 + 1;
                    }
                }
                if (c == 4) return true;
            }
        }
    }
    return false;
}
int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> a[i][j];
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j];
        }
    }
    l = 0;
    r = 1e6;
    while (l < r) {
        M = l + r + 1 >> 1;
        if (chk(M)) l = M;
        else r = M - 1;
    }
    cout << l << endl;
    return 0;
}
参考程序二(枚举+dp):
#include <bits/stdc++.h>
using namespace std;
int f[100], ix, jx, kx, n, m, a[100][100], sum[100][100], ans;
int min(int a, int b) {
    if (a < b) return a;
    return b;
}
int getsum(int x1, int y1, int x2, int y2) {
    return sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 - 1] + sum[x1 - 1][y1 - 1];
}
int getval1(int p, int r) {
    int v1 = min(getsum(1, 1, ix, p), getsum(1, p + 1, ix, r));
    int v2 = min(getsum(ix + 1, 1, jx, p), getsum(ix + 1, p + 1, jx, r));
    int v3 = min(getsum(jx + 1, 1, kx, p), getsum(jx + 1, p + 1, kx, r));
    int v4 = min(getsum(kx + 1, 1, n, p), getsum(kx + 1, p + 1, n, r));
    return min(min(v1, v2), min(v3, v4));
}
int getval2(int p, int l) {
    int v1 = min(getsum(1, p, ix, m), getsum(1, l, ix, p - 1));
    int v2 = min(getsum(ix + 1, p, jx, m), getsum(ix + 1, l, jx, p - 1));
    int v3 = min(getsum(jx + 1, p, kx, m), getsum(jx + 1, l, kx, p - 1));
    int v4 = min(getsum(kx + 1, p, n, m), getsum(kx + 1, l, n, p - 1));
    return min(min(v1, v2), min(v3, v4));
}
int calc() {
    int j = 1, ans, buf, val;
    f[1] = 0;
    for (int i = 2; i <= m; i++) {
        val = getval1(j, i);
        while (j + 1 < i && (buf = getval1(j + 1, i)) >= val) {
            j++;
            val = buf;
        }
        f[i] = val;
    }
    ans = 0;
    j = m;
    for (int i = m - 1; i >= 2; i--) {
        val = getval2(j, i);
        while (j - 1 > i && (buf = getval2(j - 1, i)) >= val) {
            j--;
            val = buf;
        }
        ans = max(ans, min(f[i - 1], val));
    }
    return ans;
}
int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> a[i][j];
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j];
        }
    }
    ans = 0;
    for (ix = 1; ix < n; ix++) {
        for (jx = ix + 1; jx < n; jx++) {
            for (kx = jx + 1; kx < n; kx++) {
                ans = max(ans, calc());
            }
        }
    }
    cout << ans << endl;
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值