Gym - 101606L Lizard Lounge(最长上升子序)

5 篇文章 0 订阅
4 篇文章 0 订阅

Monitor lizards are a kind of reptile known mainly for their cold-bloodedness and addiction to
computer screens. Due to their love for digital displays, these scaly creatures spend most of their
time at home glued to a small television in the lounge.
Conflict has arisen at one particular reptile house. The audience here has grown so large that not
everyone will be able to see the screen at once any more; specifically, a lizard will only be able
to see enough if it is strictly taller than all of the lizards sitting exactly along the straight line
from itself to the television.
Monitor lizards aren’t particularly picky about the actual contents of the screen or being able to
see it obliquely (or even from the front)—they just want to be able to keep an eye on it.
The lizards don’t want to move, however. It’s possible to chase a monitor lizard away in order
for the ones behind it to see, or leave it alone, but re-homing somewhere else in the room is
unthinkable.
Assuming lizards are removed optimally, how many at most can remain and still see the screen?
Input
• one line containing the space-separated integers TX and TY (−106 ≤ TX, TY ≤ 106), the
co-ordinates of the television.
• one line containing the integer N (1 ≤ N ≤ 106), the number of lizards.
• N further lines, each containing three space-separated integers XiYiHi (−106 ≤ X, Y ≤106; 1 ≤H ≤ 106), the co-ordinates and height respectively of one lizard.
The co-ordinates of all televisions and lizards will be distinct.
Output
Output the maximum number of lizards that can stay and watch television at once.

Sample Input
50 50
2
60 50 1
65 50 2
Sample Output
2

这估计是我这种菜鸡做过最复杂一题了。房间里的蜥蜴要盯着显示屏,在同一条直线上的蜥蜴会被前面的高蜥蜴挡住。现在可以把那些挡视线的蜥蜴丢出去,问最多能让多少蜥蜴同时看到显示屏。

那么在判蜥蜴数之前,首先就有一个步骤,就是把同一条直线上的蜥蜴丢到一起。我们用斜率来判断他们是不是在同一条直线上。之后要把他们按照在房间里的次序来排序,也就是按到显示器的距离来排序。

为了方便计算,我们把显示器修正成原点。也就是坐标系的移动:

long x = reader.nextLong() - mainX;
long y = reader.nextLong() - mainY;

x和y是蜥蜴坐标,mainX和mainY是显示器坐标。

这样一来,距离公式就变成了简易的根号下x平方+y平方,斜率也变成了y/x,算起来很舒服。

然后我们考虑到,由于这是一个360度的平面坐标,如果有两个点在不同的象限,就算他们在同一直线上(斜率相同)也是互不干扰的,所以,首先必须把所有蜥蜴给分一下象限。

if (x >= 0 && y >= 0) {
    graph[0].add(new Liz(x, y, hight));
    continue;
}
if (x <= 0 && y >= 0) {
    graph[1].add(new Liz(x, y, hight));
    continue;
}
if (x <= 0 && y <= 0) {
    graph[2].add(new Liz(x, y, hight));
    continue;
}
if (x >= 0 && y <= 0) {
    graph[3].add(new Liz(x, y, hight));
}

然后仅把同一象限的蜥蜴一起处理。这时候又想到,如果有点在坐标轴上怎么办呢?也要一起处理吗?其实只要保证把不同坐标轴的点分到不同的集合,把同一坐标轴的点分到同一集合就可以了,所以上面的代码完全够用。
(两天后注:我发现我当时居然傻傻的忘了斜率不存在的情况!!!居然ac了!!!出题人真是好人啊!点在x轴上要特殊讨论!这次过了真的只是运气好。)

之后,分四个象限,把集合先按斜率再按距离排序,进行最长上升子序列的操作。采用二分的O(nlogn)算法。最后把每条直线的最长上升子序相加,就得到了答案。

ac代码:

public class Main {
    public static void main(String[] args) {
        Scanner reader = new Scanner(System.in);
        int mainX = reader.nextInt();
        int mainY = reader.nextInt();
        int n = reader.nextInt();
        @SuppressWarnings("unchecked")
        ArrayList<Liz> graph[] = new ArrayList[4];
        for (int i = 0; i < graph.length; i++) {
            graph[i] = new ArrayList<Liz>();
        }
        for (int i = 0; i < n; i++) {
            long x = reader.nextLong() - mainX;
            long y = reader.nextLong() - mainY;
            int hight = reader.nextInt();
            if (x >= 0 && y >= 0) {
                graph[0].add(new Liz(x, y, hight));
                continue;
            }
            if (x <= 0 && y >= 0) {
                graph[1].add(new Liz(x, y, hight));
                continue;
            }
            if (x <= 0 && y <= 0) {
                graph[2].add(new Liz(x, y, hight));
                continue;
            }
            if (x >= 0 && y <= 0) {
                graph[3].add(new Liz(x, y, hight));
            }
        }

        int sum = 0;
        for (int i = 0; i < 4; i++) {
            Collections.sort(graph[i]);
            for (int j = 0; j < graph[i].size(); j++) {
                ArrayList<Integer> line = new ArrayList<>();
                double pattern = graph[i].get(j).k;
                for (int k = j; k < graph[i].size() && graph[i].get(k).k == pattern; k++) {
                    line.add(graph[i].get(k).hight);
                    j = k;
                }
                sum += getLength(line);
            }
        }
        System.out.println(sum);
    }

    static int[] arrayOut = new int[1000005];
    public static int getLength(ArrayList<Integer> arrayIn) { // 获取最长递增子序列的长度
        int position;
        int len = 1;
        for (int i = 0; i < arrayIn.size(); i++) {
            arrayOut[i] = 0;
        }
        arrayOut[1] = arrayIn.get(0); // 初始化,长度为1的LIS末尾为arrayIn[0]
        for (int i = 1; i < arrayIn.size(); i++) {
            position = BinarySearchPosition(1, len, arrayIn.get(i));
            arrayOut[position] = arrayIn.get(i);
            if (len < position) {
                len = position;
            }
        }
        return len;
    }

    public static int BinarySearchPosition(int left, int right, int key) { // 二分查找要替换的位置
        int mid;
        if (arrayOut[right] < key) {
            return right + 1;
        } else {
            while (left < right) {
                mid = (left + right) / 2;
                if (arrayOut[mid] < key) {
                    left = mid + 1;
                } else {
                    right = mid;
                }
            }
            return left;
        }
    }
}

class Liz implements Comparable<Liz> {
    long x;
    long y;
    int hight;
    double dis;
    double k;

    public Liz(long x, long y, int hight) {
        this.x = x;
        this.y = y;
        this.hight = hight;
        this.dis = Math.sqrt(x * x + y * y);
        this.k = (double) y / x;
    }

    @Override
    public int compareTo(Liz o) {
        if (this.k > o.k) {
            return -1;
        } else if (this.k < o.k) {
            return 1;
        }
        if (this.dis > o.dis) {
            return 1;
        } else if (this.dis < o.dis) {
            return -1;
        }
        return 0;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值