網易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;
}

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com