算法竞赛训练指南,52页,枚举,线性扫描
本题要点:
1、每个点,先按x坐标排序,
算出单独的y左边有m个,然后遍历 y坐标的上届和下届
for(int a = 0; a < m; ++a) // 下届
{
for(int b = a + 1; b < n; ++b) //上届
{}
}
在上届和下届确定的情况下, 求出一个长方形包含最多的点数
2、几个数组的意义, 数组的坐标指的是,该点的x坐标,排在所有单独坐标的排名, 每一个单独坐标对应一条竖线:
Left[MaxN] //Left[i] 表示第i条竖线左边有多少点在 ymax上届和下届ymin
on[MaxN] //on[i] 表示有多少点在第i条竖线上面,而且这些点的 y坐标 满足 ymin < y && ymax > y
on2[MaxN] //on2[i] 表示有多少点在第i条竖线上面,而且这些点的 y坐标 满足 ymin <= y && ymax => y
当确定x坐标的左右边界 i 和 j之后,矩形的点的最大值为 Left[j] - Left[i] +on[i] +on2[j], 遍历取最大值即可
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MaxN = 110;
int y[MaxN], on[MaxN], on2[MaxN], Left[MaxN];
int n;
int m; // 独立的y坐标
struct Point
{
int x, y;
bool operator<(const Point & rhs) const
{
return x < rhs.x;
}
}points[MaxN];
int solve()
{
sort(points, points + n);
sort(y, y + n);
int m = unique(y, y + n) - y;
if(m <= 2)
{
return n;
}
int ans = -1;
for(int a = 0; a < m; ++a)
{
for(int b = a + 1; b < n; ++b)
{
int ymin = y[a], ymax = y[b];
// 更新当前的 Left[i], on[i], on2[i]
int k = 0; // 单独的 x 坐标
for(int i = 0; i < n; ++i)
{
if(i == 0 || points[i].x != points[i - 1].x)// 一条新的竖线
{
++k;
on[k] = on2[k] = 0;
Left[k] = k == 0? 0 : Left[k - 1] + on2[k - 1] - on[k - 1];
}
if(ymin < points[i].y && ymax > points[i].y)
{
on[k]++;
}
if(ymin <= points[i].y && ymax >= points[i].y)
{
on2[k]++;
}
}
if(k <= 2)
{
return n;
}
int M = 0;
for(int j = 1; j <= k; ++j)
{
ans = max(ans, Left[j] + on2[j] + M);
M = max(M, on[j] - Left[j]);
}
}
}
return ans;
}
int main()
{
int t = 0;
while(scanf("%d", &n) != EOF && n)
{
for(int i = 0; i < n; ++i)
{
scanf("%d%d", &points[i].x, &points[i].y);
y[i] = points[i].y;
}
printf("Case %d: %d\n", ++t, solve());
}
return 0;
}
/*
10
2 3
9 2
7 4
3 4
5 7
1 5
10 4
10 6
11 4
4 6
0
*/
/*
Case 1: 7
*/