• 团体程序设计天梯赛练习集 PAT-L3-009 长城 凸包


    题目链接:https://www.patest.cn/contests/gplt/L3-009

    这道题拖了好久才AC掉。之前想的好几种贪心思路都是错的,不过也能拿26分。(测试点分值的分布好诡异……)

    错误的贪心思路之一:从右往左处理时,只考虑上一个监视点。可以构造出这样一个反例:

    蓝色的点不能被左边的监视点覆盖,但可以被右边的监视点覆盖。

    本题的正解是:维护一个上凸的半凸包:

    截止到绿色节点时,凸包的形状如黄色虚线所示,两个黄色节点是监视点。

    然后我们继续向左处理,当前节点(蓝色)可以直接加入凸包,而不需要移除其中任何节点。因此不难发现,现有的两个监视点不能覆盖当前位置,我们必须将绿色节点也设为监视点。

    继续向左,将当前节点加入凸包时需要移除其中两个节点,如下图:

    由图可知当前节点可以被监视。

    继续这个过程:

    最后一张图时,将当前节点加入凸包不需要移除其中任何节点,说明当前位置无法受监视,需要将绿色节点也设为监视点。

    综上可知,在维护上凸的半凸包时,如果加入某个节点不会导致凸包里其他节点被移除,那么之前一个节点就应该被设为监视点,答案+1。

    代码如下:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    
    using namespace std;
    
    struct Point { int x, y; };
    
    vector<Point> stk;
    int ans = 0;
    int N;
    
    long long multi(int x1, int y1, int x2, int y2)
    {
        return 1LL * x1 * y2 - 1LL * x2 * y1;
    }
    
    int main()
    {
        int x, y, lx, ly;
        scanf("%d", &N);
        scanf("%d%d%d%d", &x, &y, &lx, &ly);
        stk.push_back({x, y});
        stk.push_back({lx, ly});
    
        for (int i = 3; i <= N; i++)
        {
            scanf("%d%d", &x, &y);
            bool ok = false;
            while (stk.size() >= 2)
            {
                Point &p1 = stk.back();
                Point &p2 = stk[stk.size() - 2];
                if (multi(p1.x - p2.x, p1.y - p2.y, x - p1.x, y - p1.y) <= 0)
                {
                    ok = true;
                    stk.pop_back();
                }
                else break;
            }
            if (!ok)
                ans += 1;
            stk.push_back({x, y});
        }
    
        printf("%d", ans);
        return 0;
    }
  • 相关阅读:
    cocos2d游戏jsc文件格式解密,SpideMonkey大冒险
    抖音下载短视频去水印方法
    Metaspliot技术
    WAF bypass
    博客园美化
    Redis未授权访问利用
    网站后台getshell
    OpenVAS
    跨站脚本攻击与防御总结
    相同浏览器同一浏览器多用户登录问题
  • 原文地址:https://www.cnblogs.com/Onlynagesha/p/8672128.html
Copyright © 2020-2023  润新知