题目
农夫约翰想用他购买的新监视系统监视他的 N 头奶牛。
第 i 头奶牛位于具有整数坐标的位置 (xi,yi)。
没有两头奶牛占据相同的位置。
约翰的监视系统包含三个特殊的摄像头,每个摄像头都能够沿垂直或水平线观察一条线上所有的奶牛。
请确定约翰是否可以通过合理设置三个摄像头的摆放位置,以便他可以监视所有 N 头奶牛。
也就是说,请确定是否可以通过三条垂直或水平摆放的线将所有 N 个奶牛所在位置全部覆盖。
输入格式
第一行包含整数 N。
接下来 N 行,每行包含两个整数 xi,yi,表示一头奶牛所在位置的横纵坐标。
输出格式
如果可以通过三条垂直或水平摆放的线将所有 N 个奶牛所在位置全部覆盖,请输出 1,否则输出 0。
数据范围
1 ≤ N ≤ 50000,
0 ≤ xi, yi ≤109
输入样例:
6
1 7
0 0
1 2
2 0
1 4
3 4
输出样例:
1
样例解释
y=0,x=1,y=4 可满足将所有奶牛位置覆盖。
解题思路
用三条直线覆盖所有的点,有以下几种情形:
(1)三条平行于 x 轴的直线
(2)三条平行于 y 轴的直线
(3)一条平行于 x 轴的直线和两条平行于 y 轴的直线
(4)一条平行于 y 轴的直线和两条平行于 x 轴的直线
所以我们先记录 x 位置出现的数字和 y 位置出现的数字
如果 x 出现的数字数量小于等于 3,那么我们可以用小于等于三条平行于 y 轴的直线覆盖,即情形 2
如果 y 出现的数字数量小于等于 3,那么我们可以用小于等于三条平行于 x 轴的直线覆盖,即情形 1
枚举 x 出现的所有数字,先用 x = xi 这条直线覆盖,将剩下未被覆盖的牛用平行于 x 轴的直线覆盖
若所需要的直线数小于等于三,则该方案可行,有解
枚举 y 出现的所有数字,先用 y = yi 这条直线覆盖,将剩下未被覆盖的牛用平行于 y 轴的直线覆盖
若所需要的直线数小于等于三,则该方案可行,有解
Java代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Set;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(System.out);
int n = Integer.parseInt(in.readLine()); // 牛的数量
Cow[] cows = new Cow[n];
Set<Integer> rowSet = new HashSet<Integer>(); // 存储行的位置
Set<Integer> columnSet = new HashSet<Integer>(); // 存储列的位置
boolean flag = false; // 初始状态为不可用三条线覆盖
for(int i = 0; i < n; i++) {
String str = in.readLine();
int x = Integer.parseInt(str.split(" ")[0]);
int y = Integer.parseInt(str.split(" ")[1]);
cows[i] = new Cow(x, y);
rowSet.add(x);
columnSet.add(y);
}
// 如果行的位置数或者列的位置数小于等于三,则说明可以用三条或三条以下的平行直线覆盖
if(rowSet.size() <= 3 || columnSet.size() <= 3) {
out.println(1);
out.flush();
return ;
}
// 先用一条平行于 x 轴的直线覆盖
for(int row : rowSet) {
Set<Integer> temp = new HashSet<Integer>();
for(int i = 0; i < n; i++) {
if(cows[i].x != row) temp.add(cows[i].y); // 如果该点未被横线覆盖,则用一条平行于 y 轴的直线覆盖
if(temp.size() > 2) break; // 若该方案覆盖的平行于 y 轴的直线已经超过两条,说明该方案不可行
}
if(temp.size() <= 2) {
flag = true; // 若该方案覆盖的平行于 y 轴的直线小于等于两条,说明该方案可行
break;
}
}
if(flag) {
out.print(1);
out.flush();
return;
}
// 再用一条平行于 y 轴的直线覆盖
for(int column : columnSet) {
Set<Integer> temp = new HashSet<Integer>();
for(int i = 0; i < n; i++) {
if(cows[i].y != column) temp.add(cows[i].x); // 如果该点未被覆盖,则用一条平行于 x 轴的直线覆盖
if(temp.size() > 2) break ; // 若该方案覆盖的平行于 x 轴的直线已经超过两条,说明该方案不可行
}
if(temp.size() <= 2) {
flag = true; // 若该方案覆盖的平行于 x 轴的直线小于两条,则说明该方案可行
break;
}
}
if(flag) out.print(1);
else out.print(0);
out.flush();
}
public static class Cow{
public int x; // 牛所在的行
public int y; // 牛所在的列
public Cow() {
this.x = 0;
this.y = 0;
}
public Cow(int x, int y) {
this.x = x;
this.y = y;
}
}
}