团体程序设计天梯赛练习集 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;
}

 

转载于:https://www.cnblogs.com/Onlynagesha/p/8672128.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值