POJ1066

题意:有一个100x100的正方形,其实也就是有四面墙围成的一个图形,正方形中间有一个点,也就是宝藏所在的位置,除此之外还有内墙,为你从外面到宝藏哪里,最少炸几面墙就可以到达
解析:枚举每一面内墙的起点和终点与宝藏连线,判断和几面内墙相交,除此之外还要枚举正方形的四个端点.
特别注意当判断了枚举的直线与线段相交以后要注意判断交点是否在线段上。
(似乎还有一种做法,bfs,先枚举线段所有的中点,然后以墓碑为起点bfs,如果两点之间组成的线段于所有的墙没有交点的话(除了它们本身所在的墙),则加入bfs。直到最先搜索到的点在正方形上时,就是最少的步数。)

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
const int MAXN = 100000;
const double EPS = 1e-8;

// 带误差比较
inline bool dcmp(double x, double y = 0)
{
    return fabs(x - y) <= EPS;
}

/*
 * 向量(Vector)或点
 *
 * 使用原点到一个点 (x, y) 的有向线段表示向量
 * 从点 A 到点 B 的向量表示为 A - B
 */
typedef struct Vec
{
    double x, y;

    Vec(double x = 0, double y = 0) : x(x), y(y) {}

    // 相加
    Vec operator+(const Vec &v) const
    {
        return Vec(x + v.x, y + v.y);
    }

    // 相减
    Vec operator-(const Vec &v) const
    {
        return Vec(x - v.x, y - v.y);
    }

    // 数乘(伸长、缩短)
    Vec operator*(double d) const
    {
        return Vec(x * d, y * d);
    }

    Vec operator/(const double d) const
    {
        return Vec(x / d, y / d);
    }
    bool operator==(const Vec&v) const{
        return (x==v.x&&y==v.y);
    }
    // 范数,用来比较长度,等于长度的平方
    double norm() const
    {
        return x * x + y * y;
    }
} Pt;

// 点乘
double dot(const Vec &a, const Vec &b)
{
    return a.x * b.x + a.y * b.y;
}

// 叉乘
double cross(const Vec &a, const Vec &b)
{
    return a.x * b.y - a.y * b.x;
}

// 线段(Segment),用两个点表示
struct Seg
{
    Pt a, b;

    Seg() : a(0), b(0) {}

    // 线段包含点(点在线段上)
    bool include(const Pt &p)
    {
        // PA × PB = 0:PA 与 PB 共线,即点在线段所在的直线上
        // PA · PB = 0:PA 与 PB 方向不同(A 和 B 分别在 P 的两边),如果 PA · PB = 0 则 P = A 或 P = B
        return dcmp(cross(a - p, b - p)) && dot(a - p, b - p) <= 0;
    }
};

// 直线,用两个点表示
struct Line
{
    Pt a, b;

    Line() {} // 提供一个不需要参数的构造函数
    Line(const Pt &a, const Pt &b) : a(a), b(b) {}

    bool include(const Pt &p) const
    {
        return dcmp(cross(a - p, b - p));
    }

    // 两直线关系(交点个数)
    // 0 表示平行(无交点)
    // 1 表示相交(一个交点)
    // -1 表示重合(无数个交点)
    static int relation(const Line &a, const Line &b)
    {
        if (a.include(b.a) && a.include(b.b)) return -1;
        else if (dcmp(cross(a.b - a.a, b.b - b.a))) return 0;
        else return 1;
    }

    // 求两直线交点(需要保证两直线有交点)
    static Pt intersect(const Line &a, const Line &b)
    {
        double s1 = cross(b.a - a.a, b.b - a.a), s2 = cross(b.b - a.b, b.a - a.b);
        return a.a + (a.b - a.a) * s1 / (s1 + s2);
    }
};
double cross(Pt a,Pt b,Pt c)//直线叉积,前面两个点是直线上的点,最后那个点是线段上的点,本题一定要用这个叉乘
{
    return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
int main()
{
    int n,i,j,k;Pt p1,p2,target;
    cin>>n;
    vector<Seg>seg;vector<Pt>pt;
    for(i=1;i<=n;i++){
        scanf("%lf%lf%lf%lf",&p1.x,&p1.y,&p2.x,&p2.y);
        //if(p1.x==0||p1.x==100||p1.y==0||p1.y==100)
        pt.push_back(p1);
        //if(p2.x==0||p2.x==100||p2.y==0||p2.y==100)
        pt.push_back(p2);Seg seg1;seg1.a=p1;seg1.b=p2;
        seg.push_back(seg1);
    }
    pt.push_back(Pt{0,0});pt.push_back(Pt{0,100});pt.push_back(Pt{100,100});pt.push_back(Pt{100,0});
    scanf("%lf%lf",&target.x,&target.y);
    int minn=(1<<31)-1;
    for(i=0;i<pt.size();i++){
        Line t1,t2;t1.a=target;t1.b=pt[i];int cnt=0;
        for(j=0;j<seg.size();j++){
            //if(seg[j].a==t1.a&&seg[j].b==t1.b)continue;
            if(cross(t1.a,t1.b,seg[j].a)*cross(t1.a,t1.b,seg[j].b)<0) {
                Seg temp1;temp1.a=t1.a;temp1.b=t1.b;t2.a=seg[j].a;t2.b=seg[j].b;
                Pt p = t1.intersect(t1, t2);//判断交点是否在线段上
                if (temp1.include(p) && seg[j].include(p))
                    cnt++;
            }
//            t2.a=seg[j].a;t2.b=seg[j].b;
//            int re=t1.relation(t1,t2);Seg temp{t1.a,t1.b};//temp.a=t1.a;temp.b=t1.b;
//            if(re==0)continue;
//            else if(re==-1){
//                //if(temp.include(seg[i].a)||temp.include(seg[i].b)||seg[i].include(temp.a)||seg[i].include(temp.b))
//                    cnt++;
//            }
//            else{
//                Pt p=t1.intersect(t1,t2);
//                if(temp.include(p)&&seg[i].include(p))cnt++;
//            }
        }
        minn=min(minn,cnt);
    }
    cout<<"Number of doors = ";
    cout<<minn+1<<endl;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值