挑战程序竞赛系列(86):3.6极限情况(3)

挑战程序竞赛系列(86):3.6极限情况(3)

传送门:AOJ 2201: Immortal Jewels

翻译参考至hankcs: http://www.hankcs.com/program/algorithm/aoj-2201-immortal-jewels.html

题意:

求婚:有个贵族向一个贫穷的公主求婚,公主提出条件,需要一种“永生宝石”做嫁妆。这种宝石极其稀有,而且极易损毁,所以开采时需要特别小心。如图:

矿工需要使用一种特殊的金属棒开采,宝石呈圆形,矿床是二维平面,每颗宝石坐标为x,y,半径为r,能够吸附在距离m内的金属棒上。一旦金属棒穿过某个宝石,该宝石就被破坏了。

金属棒非常昂贵,只有一根,作为贵族雇佣的程序员,请你帮他算出能开采到的最大宝石数? 数据格式如下: N x1 y1 r1 m1 x2 y2 r2 m2 … xN yN rN mN 由多个用例组成,输出答案并换行。

直接参考hankcs:

对每个宝石圆,都添加半径扩大m_i的磁力范围圆,将两个圆同时纳入考虑,移动直线的过程中,如果能取到的宝石个数发生变化,那么直线肯定与所有圆中的两个相切,这就是极限情况。所以枚举两个圆的组合确定一条直线,取最大值就行了。

此题的麻烦之处在于求解两个圆的外接切线和内接切线,具体直接参考代码吧。

代码如下:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

public class Main{

    String INPUT = "./data/judge/201709/A2201.txt";

    public static void main(String[] args) throws IOException {
        new Main().run();
    }

    static final double PI  = Math.acos(-1);
    static final double EPS = 1E-12;

    class Point{

        double x;
        double y;

        Point(double x, double y){
            this.x = x;
            this.y = y;
        }

        Point add(Point a) {
            return new Point(x + a.x, y + a.y);
        }

        Point sub(Point a) {
            return new Point(x - a.x, y - a.y);
        }

        Point rot(double rad) {
            double a = Math.cos(rad);
            double b = Math.sin(rad);
            return new Point(x * a - b * y, x * b + a * y);
        }

        double abs() {
            return Math.sqrt(x * x + y * y);
        }
    }

    double dist(Point a, Point b) {
        double dx = a.x - b.x;
        double dy = a.y - b.y;
        return Math.sqrt(dx * dx + dy * dy);
    }

    double dot(Point a, Point b) {
        return a.x * b.y - a.y * b.x;
    }

    class Line{

        Point a;
        Point b;

        Line(Point a, Point b){
            this.a = a;
            this.b = b;
        }

        double distance(Point p) {
            double d = dist(a, b);
            return Math.abs(dot(p.sub(a), b.sub(a)) / d);
        }
    }

    class Circle{

        Point o;
        double r;

        Circle(Point o, double r){
            this.o = o;
            this.r = r;
        }

        // 通过点p 的两条切线
        List<Point> tangent(Point p){
            double L = dist(o, p);
            double M = Math.sqrt(L * L - r * r);
            double theta = Math.asin(r / L);
            Point v = o.sub(p);
            v.x /= L;
            v.y /= L;
            List<Point> ans = new ArrayList<>();
            Point t = v.rot(theta);
            t.x *= M;
            t.y *= M;
            ans.add(p.add(t));
            t = v.rot(-theta);
            t.x *= M;
            t.y *= M;
            ans.add(p.add(t));
            return ans;
        }

        // 两个半径相等圆的两条平行外切线
        List<Line> outer_tangent_parallel(Circle c){
            Point d = o.sub(c.o);
            Point v = new Point(-r / d.abs() * d.y, r / d.abs() * d.x);
            List<Line> lines = new ArrayList<>();
            lines.add(new Line(o.add(v), c.o.add(v)));
            lines.add(new Line(o.sub(v), c.o.sub(v)));
            return lines;
        }

        // 两圆外切线
        List<Line> outer_tangent(Circle c){
            if (cmp(r, c.r) == 0) {
                return outer_tangent_parallel(c);
            }
            if (cmp(r, c.r) == 1) {
                return c.outer_tangent(this);
            }
            Point d = o.sub(c.o);
            double fact = c.r / r - 1;
            Point base = c.o.add(d).add(new Point(d.x / fact, d.y / fact));
            List<Point> ps = tangent(base);
            List<Line> ans = new ArrayList<>();
            ans.add(new Line(base, ps.get(0)));
            ans.add(new Line(base, ps.get(1)));
            return ans;
        }

        // 两圆内切线
        List<Line> inner_tangent(Circle c){
            if (cmp(r, c.r) == 1) {
                return c.inner_tangent(this);
            }
            Point d = c.o.sub(o);
            double fact = c.r / r + 1;
            Point base = o.add(new Point(d.x / fact, d.y / fact));
            List<Point> ps = tangent(base);
            List<Line> ans = new ArrayList<>();
            ans.add(new Line(base, ps.get(0)));
            ans.add(new Line(base, ps.get(1)));
            return ans;
        }

        // 两个圆的交点
        Line intersection(Circle c) {
            double d = dist(o, c.o);
            double cos = Math.cos((d * d + r * r - c.r * c.r) / (2 * d * r));
            Point e = c.o.sub(o);
            e.x /= d;
            e.y /= d;
            Point t1 = e.rot(cos);
            t1.x *= r;
            t1.y *= r;
            Point t2 = e.rot(-cos);
            t2.x *= r;
            t2.y *= r;
            return new Line(o.add(t1), o.add(t2));
        }

        // 是否相离
        boolean independent(Circle c) {
            return cmp(dist(o, c.o), r + c.r) > 0;
        }

        // 是否包含圆c
        boolean contains(Circle c) {
            return cmp(dist(o, c.o) + c.r, r) < 0;
        }

        boolean intersects(Circle c) {
            return !contains(c) && !c.contains(this) && !independent(c);
        }
    }

    class Pair{

        Circle fir;
        Circle sec;

        Pair(Circle fir, Circle sec){
            this.fir = fir;
            this.sec = sec;
        }
    }

    int cmp(double a, double b) {
        double diff = a - b;
        if (Math.abs(diff) < EPS) return 0;
        else if (diff < 0) return -1;
        else return 1;
    }

    List<Pair> jewels;
    List<Line> lines;
    int N;

    void constructLine(Circle c1, Circle c2, List<Line> lines) {
        // 两圆相交 && 两圆相离
        if (c1.independent(c2)) { // 相离
            List<Line> outer = c1.outer_tangent(c2);
            lines.add(outer.get(0));
            lines.add(outer.get(1));
            List<Line> inner = c1.inner_tangent(c2);
            lines.add(inner.get(0));
            lines.add(inner.get(1));
        }

        if (c1.intersects(c2)) {  // 相交
            List<Line> outer = c1.outer_tangent(c2);
            lines.add(outer.get(0));
            lines.add(outer.get(1));
            Line inter = c1.intersection(c2);
            lines.add(inter);
        }
    }

    int count(List<Pair> jewels, Line line) {
        int cnt = 0;
        for (Pair j : jewels) {
            if (cmp(j.fir.r, line.distance(j.fir.o)) <= 0 && cmp(j.sec.r, line.distance(j.sec.o)) >= 0) {
                cnt ++;
            }
        }
        return cnt;
    }

    void read() {
        while (true) {
            N = ni();
            if (N == 0) break;

            jewels = new ArrayList<>();
            lines  = new ArrayList<>();

            for (int i = 0; i < N; ++i) {
                double x, y, r, m;
                x = nd();
                y = nd();
                r = nd();
                m = nd();
                Pair jewel = new Pair(new Circle(new Point(x, y), r), new Circle(new Point(x, y), r + m));
                for (Pair p : jewels) {
                    constructLine(jewel.fir, p.fir, lines);
                    constructLine(jewel.fir, p.sec, lines);
                    constructLine(jewel.sec, p.fir, lines);
                    constructLine(jewel.sec, p.sec, lines);
                }
                jewels.add(jewel);
            }

            int ans = 1;
            for (Line line : lines) {
                ans = Math.max(ans, count(jewels, line));
            }
            out.println(ans);
        }
    }

    FastScanner in;
    PrintWriter out;

    void run() throws IOException {
        boolean oj;
        try {
            oj = ! System.getProperty("user.dir").equals("F:\\java_workspace\\leetcode");
        } catch (Exception e) {
            oj = System.getProperty("ONLINE_JUDGE") != null;
        }

        InputStream is = oj ? System.in : new FileInputStream(new File(INPUT));
        in = new FastScanner(is);
        out = new PrintWriter(System.out);
        long s = System.currentTimeMillis();
        read();
        out.flush();
        if (!oj){
            System.out.println("[" + (System.currentTimeMillis() - s) + "ms]");
        }
    }

    public boolean more(){
        return in.hasNext();
    }

    public int ni(){
        return in.nextInt();
    }

    public long nl(){
        return in.nextLong();
    }

    public double nd(){
        return in.nextDouble();
    }

    public String ns(){
        return in.nextString();
    }

    public char nc(){
        return in.nextChar();
    }

    class FastScanner {
        BufferedReader br;
        StringTokenizer st;
        boolean hasNext;

        public FastScanner(InputStream is) throws IOException {
            br = new BufferedReader(new InputStreamReader(is));
            hasNext = true;
        }

        public String nextToken() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (Exception e) {
                    hasNext = false;
                    return "##";
                }
            }
            return st.nextToken();
        }

        String next = null;
        public boolean hasNext(){
            next = nextToken();
            return hasNext;
        }

        public int nextInt() {
            if (next == null){
                hasNext();
            }
            String more = next;
            next = null;
            return Integer.parseInt(more);
        }

        public long nextLong() {
            if (next == null){
                hasNext();
            }
            String more = next;
            next = null;
            return Long.parseLong(more);
        }

        public double nextDouble() {
            if (next == null){
                hasNext();
            }
            String more = next;
            next = null;
            return Double.parseDouble(more);
        }

        public String nextString(){
            if (next == null){
                hasNext();
            }
            String more = next;
            next = null;
            return more;
        }

        public char nextChar(){
            if (next == null){
                hasNext();
            }
            String more = next;
            next = null;
            return more.charAt(0);
        }
    }
}

Java的确没有C++方便,少了操作符重载和Pair只能靠自己写函数,显得代码很冗长。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏机器学习入门

挑战程序竞赛系列(88):3.6平面扫描(2)

挑战程序竞赛系列(88):3.6平面扫描(2) 传送门:POJ 3168: Barn Expansion 题意: 求不重叠的矩形个数。 当然,题意是说求能够被...

1755
来自专栏xingoo, 一个梦想做发明家的程序员

AOE关键路径

这个算法来求关键路径,其实就是利用拓扑排序,首先求出,每个节点最晚开始时间,再倒退求每个最早开始的时间。 从而算出活动最早开始的时间和最晚开始的时间,如果这两个...

1947
来自专栏跟着阿笨一起玩NET

读取txt正则匹配行写入txt

591
来自专栏xingoo, 一个梦想做发明家的程序员

二项队列

二项队列是 堆序 的集合,也叫 森林。其中每一种形式都有约束。 二项树Bk由一个带有儿子的B0,B1,B2...组成,高度为k的二项树 恰好有2^k个结点。每一...

2615
来自专栏技术专栏

无向图最短路径问题升级版

问题: 无向图G有N个结点,它的边上带有正的权重值。 你从结点1开始走,并且一开始的时候你身上带有M元钱。如果你经过结点i, 那么你就要花掉S[i]元(可以把...

754
来自专栏海天一树

约瑟夫环的三种解法

约瑟夫环(Josephus)问题是由古罗马的史学家约瑟夫(Josephus)提出的,他参加并记录了公元66—70年犹太人反抗罗马的起义。约瑟夫作为一个将军,设法...

842
来自专栏机器学习入门

挑战程序竞赛系列(82):4.3 LCA(2)

挑战程序竞赛系列(82):4.3 LCA(2) 传送门:POJ 1986: Distance Queries 题意: LCA距离:快速查询树中任意两个节点间...

17110
来自专栏xingoo, 一个梦想做发明家的程序员

AOV网络拓扑排序

这个算法,主要是为输出一个无环图的拓扑序列 算法思想: 主要依赖一个栈,用来存放没有入度的节点,每次读取栈顶元素,并将栈顶元素的后继节点入度减一,如果再次出现入...

1875
来自专栏恰同学骚年

数据结构基础温故-1.线性表(下)

在上一篇中,我们了解了单链表与双链表,本次将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称循...

682
来自专栏烙馅饼喽的技术分享

生成透明GIF的方法

Private Shared Function CreateTransParentGif(ByVal img As Image) As Bitmap      ...

32513

扫码关注云+社区