TZOJ练习 - 1471: Wall

题目链接:1471:Wall (tzcoder.cn)


前置知识:叉积求向量方向面积,两点距离公式,Andrew凸包算法

Andrew算法简单介绍:

把所有点以横坐标为主排序,纵坐标为次排序。

排序后最小的元素和最大的元素一定在凸包上。而且因为是凸多边形,我们如果从一个点出发逆时针走,如果出现凹点(用叉积得负则出现了凹点)就删掉这个凹点,因此我们很自然想到用一个单调栈来维护上下凸壳。

因为从左向右看,上下凸壳所旋转的方向不同,为了让单调栈起作用,我们首先 从头枚举 求出下凸壳,然后从尾反向求出上凸壳。


本题是直接套用以上算法思路的板子题。

另外对于每个点要求距离至少为m,则加上1个半径为m圆弧即可

code:

#include <bits/stdc++.h>
using namespace std;
typedef long long  ll;
typedef pair<ll, ll> PII;
const int maxn = 2005;
const int N = 1e6 + 50;
const ll mod = 1e9+7;
const int P = 131;
const ll pz = 131, py = 13331;
struct Point 
{
    double x, y;
    bool operator<(const Point s)const {
        return x != s.x ? x < s.x : y < s.y;
    }
}p[N],s[N];
const double eps = 1e-8;
int n, m, top;
double cj(Point a, Point b, Point c)
{
    return (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y);
}
double dis(Point a, Point b)
{
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
double Andrew() {
    sort(p + 1, p + n + 1);
    top = 0;
    for (int i = 1; i <= n; i++)
    {
        while (top > 1 && cj(s[top - 1], s[top], p[i]) <= eps)top--;
        s[++top] = p[i];
    }
    int t = top;
    for (int i = n - 1; i >= 1; i--)
    {
        while (top > t && cj(s[top - 1], s[top], p[i]) <= eps)top--;
        s[++top] = p[i];
    }
    double ans = 0;
    for (int i = 1; i < top; i++)ans += dis(s[i],s[i+1]);
    return ans;
}
inline void solve()
{
    while (cin >> n >> m)
    {
        for (int i = 1; i <= n; i++)
            cin >> p[i].x >> p[i].y;
        if (n == 2)
        {
            cout << fixed << setprecision(0) << dis(p[1], p[2]) * 2 + acos(-1) * 2 * m << endl;
            continue;
        }
        double ans = Andrew()+acos(-1)*2*m;
        cout << fixed << setprecision(0) << ans<< endl;
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    int t = 1;
    //cin >> t;
    while (t--)
        solve();



    return 0;

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值