欢迎访问 My Luogu Space。
【题目大意】
一个 n ∗ m n*m n∗m 的表格,每个格子里面都有一个不超过 k k k 的正整数。现在请你找出一个面积最大的矩形,使得矩形内部的不同的数字个数不超过 r r r,且出现在矩形内部的数字不能再出现在矩形外部。
【题解】
不难发现,由于我们需要求出的矩形内的数字不会再出现在矩形外部,因此矩形的左上角一定是能够完全包含这个数字的最小的矩形(不考虑出现别的数字)的左上角,其他三个角也同理。
我们先预处理出能够完整包含每个数字的最小矩形。
由于两个相对顶点确定一个矩形,我们可以 O ( n 2 ) O(n^2) O(n2) 枚举两个最小矩形作为答案矩形的两个顶点得到答案矩形,然后再 O ( n ) O(n) O(n) 枚举所有的最小矩形,判断每个最小矩形:
- 被包含在答案矩形内部
- 在答案矩形的外部
- 与答案矩形相交
如果在答案矩形内部,那么计数器加一;
如果与答案矩形相交,说明出现在矩形内部的数字也出现在了矩形的外部,与题目要求不符合,直接跳过当前的答案矩形。
如果没有跳过并且枚举完所有矩形之后,再判断计数器与 r r r 的大小关系。
最后更新答案。
效率 O ( n 3 ) O(n^3) O(n3)。
加点随机打乱和剪枝可以挤到排名很前面。
【代码】
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1000+10;
struct DATA{int u, d, l, r;}s[MAXN];
int n, m, k, r, ans, ct;
int f[MAXN], id[MAXN];
int main(){
scanf("%d%d%d%d", &n, &m, &k, &r);
for(int i=1; i<=k; ++i){
s[i].u = 1e9;
s[i].l = 1e9;
}
for(int i=1; i<=n; ++i)
for(int j=1; j<=m; ++j){
int a; scanf("%d", &a);
if(!f[a]) id[a] = ++ct, f[a] = 1;
s[id[a]].u = min(s[id[a]].u, i);
s[id[a]].d = max(s[id[a]].d, i);
s[id[a]].l = min(s[id[a]].l, j);
s[id[a]].r = max(s[id[a]].r, j);
}
for(int i=1; i<=ct; ++i)
for(int j=i; j<=ct; ++j){
int U = min(s[i].u, s[j].u);
int D = max(s[i].d, s[j].d);
int L = min(s[i].l, s[j].l);
int R = max(s[i].r, s[j].r);
if((R-L+1)*(D-U+1) <= ans) continue;
int can=1, cnt=0;
for(int x=1; x<=ct; ++x){
if(s[x].u>=U && s[x].d<=D && s[x].l>=L && s[x].r<=R) ++cnt;
else if(s[x].d<U || s[x].u>D || s[x].l>R || s[x].r<L) continue;
else can = 0;
if(!can || cnt>r) break;
}
if(can && cnt<=r) ans = max(ans, (R-L+1)*(D-U+1));
}
printf("%d", ans);
return 0;
}