【POJ-2653-Pick-up sticks】计算几何+STL

探讨了在二维平面上放置木棍后,如何找出未被覆盖的木棍。通过逆向思考,从最后一根木棍开始,递归判断哪些木棍被覆盖,最终确定顶层木棍。使用特定数据结构优化算法,降低时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接
http://poj.org/problem?id=2653
题意
在 一 个 二 维 平 面 上 依 次 放 置 n 条 木 棍 , 问 最 后 没 有 被 覆 盖 的 木 棍 有 哪 些 , n &lt; = 1 e 5 , 答 案 &lt; = 1000 在一个二维平面上依次放置n条木棍,问最后没有被覆盖的木棍有哪些,n&lt;=1e5,答案&lt;=1000 n,n<=1e5,<=1000
做法
我 们 时 光 倒 流 一 下 , 从 后 往 前 做 这 道 题 我们时光倒流一下,从后往前做这道题
很 明 显 最 后 一 根 木 棍 一 定 是 在 最 上 面 的 , 那 么 这 条 木 棍 所 覆 盖 的 木 棍 一 定 都 是 被 覆 盖 的 很明显最后一根木棍一定是在最上面的,那么这条木棍所覆盖的木棍一定都是被覆盖的
依 次 递 推 就 能 求 出 所 有 被 最 后 一 条 木 棍 直 接 覆 盖 或 者 间 接 覆 盖 的 木 棍 依次递推就能求出所有被最后一条木棍直接覆盖或者间接覆盖的木棍
然 后 继 续 向 前 扫 描 到 一 个 没 有 被 覆 盖 的 木 棍 , 然 后 重 复 这 个 过 程 然后继续向前扫描到一个没有被覆盖的木棍,然后重复这个过程
这 样 就 可 以 得 到 所 有 在 最 顶 层 的 木 棍 , 如 果 我 们 用 v i s 标 记 被 覆 盖 木 棍 的 方 法 这样就可以得到所有在最顶层的木棍,如果我们用vis标记被覆盖木棍的方法 vis
时 间 复 杂 度 为 n 2 时间复杂度为n^2 n2
但 是 如 果 我 们 用 l i s t 里 面 的 l i s t 维 护 没 有 被 覆 盖 的 木 棍 , 时 间 复 杂 度 就 是 O ( n ∗ 1000 ) 。 但是如果我们用list里面的list维护没有被覆盖的木棍,时间复杂度就是O(n*1000)。 listlistO(n1000)
代码

#include<stdio.h>
#include<math.h>
#include<queue>
#include<list>
#include<iostream>
#include<algorithm>
using namespace std;
const  double eps = 1e-12;
const  double pi = acos(-1.0);
const int maxp = 1010;
int sgn( double x)
{
    if (fabs(x) <eps) return 0;
    if(x <0) return -1;
    return 1;
}
struct Point
{
    double x, y;
    Point() {}
    Point ( double _x,  double _y)
    {
        x = _x, y = _y;
    }
    bool operator == (Point b) const
    {
        return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
    }
    bool operator < (Point b) const
    {
        return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x;
    }
    Point operator - (const Point &b) const
    {
        return Point(x - b.x, y - b.y);
    }
    //叉积  |a||b|sin
    double  operator ^ (const Point &b) const
    {
        return x * b.y - y * b.x;
    }
    //点积 |a||b|cos 向量点积之后反过来求夹角
    double  operator * (const Point & b) const
    {
        return x * b.x + y * b.y;
    }
    Point operator + (const Point &b) const
    {
        return Point(x + b.x, y + b.y);
    }
};
struct Line
{
    Point s, e;
    Line() {}
    Line(Point _s, Point _e)
    {
        s = _s, e = _e;
    }
    //两线段相交判断
    //2 规范相交
    //1 非规范相交-过端点
    //0 不相交
    int segcrossseg(Line v)
    {
        int d1 = sgn((e-s)^(v.s-s));
        int d2 = sgn((e-s)^(v.e-s));
        int d3 = sgn((v.e-v.s)^(s-v.s));
        int d4 = sgn((v.e-v.s)^(e-v.s));
        if((d1^d2)==-2&&(d3^d4)==-2) return 2;
            return ((d1==0&&sgn((v.s-s)*(v.s-e))<=0)||(d2==0&&sgn((v.e-s)*(v.e-e))<=0)||(d3==0&&sgn((s-v.s)*(s-v.e))<=0)||(d4==0&&sgn((e-v.s)*(e-v.e))<=0));
    }
};
const int maxn = 1e5+10;
Line l[maxn];
vector<int>ans;
list<int> L;
void del(list<int>::iterator it)//递归算出所有被当前线段所影响的线段
{
    Line now = l[*it];
    list<int>::iterator tmp=it;
    for(list<int>::iterator itt=(++it);itt!=L.end();)
    {
         Line nex = l[*itt];
         if(now.segcrossseg(nex))
         {
             del(itt);//找到之后先递归,再将本点删除
             itt=L.erase(itt);
         }
         else ++itt;
    }
    return ;
}
int main()
{
    int n;
    double xa,xb,ya,yb;
    while(scanf("%d",&n)!=EOF)
    {
        L.clear();
        ans.clear();
        if(n==0) break;
        for(int i=1;i<=n;i++)
        {
            scanf("%lf%lf%lf%lf",&xa,&ya,&xb,&yb);
            l[i]=Line (Point(xa,ya),Point(xb,yb));
        }
        for(int i=n;i>=1;i--)
        {
            L.push_back(i);
        }
        for(list<int>::iterator it=L.begin();it!=L.end();)
        {
            list<int>::iterator tmp=it;
            del(it);
            it=++tmp;//由于有删除操作,要先存当前指针位置,在删除之后让迭代器指向当前的下一位
        }
        L.reverse();
        for(list<int>::iterator it=L.begin();it!=L.end();++it)
        {
            int tmp=*it;
            ans.push_back(tmp);
        }
         sort(ans.begin(),ans.end());//保证答案有序
         printf("Top sticks:");
        for(int i=0;i<ans.size();i++)
        {
            if(i!=ans.size()-1) printf(" %d,",ans[i]);
            else printf(" %d.\n",ans[i]);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值