UVa1446/LA4640 Origami Through-Hole

题目链接

         本题是2009年ICPC亚洲区域赛东京赛区E

题意

        有一个100mm×100mm的纸片,进行若干次折叠操作:每次给出点P、Q,沿着P、Q的垂直平分线将包含P的部分翻折至P点在Q点上。最后给出一个点的h,从h点打孔,问纸张展开后能看到几个孔。最多折叠10次,折叠有三条规则。

        规则1:包含点P的面,最上层的那个被折叠。

        规则2:某个面被折叠时,被折叠部分连接到的面也将被折叠。

        规则3:某个面被折叠部分其上面与之有重叠的那些面也将被折叠。

分析

        切割多边形,规则1直接从上往下寻找首个P点所在的面即可,难点是实现规则2和规则3。

        每次被折叠产生的新面必然覆盖旧面的上方,并且原来处在更下的面翻折后将处在更上,这是面排序的依据,面数值可以按照排在越上层的索引越大来存储。

        为了实现规则2和规则3,每找到一个被折叠的面,先不更新折叠后的结果,而是临时计算翻折产生的新面并根据规则2和规则3拓展寻找其他的将被折叠的面,具体来说利用状态f[1024]实现递归过程:f[i]=0表示面i暂时不需要折叠,f[i]=1表示面i需要被折叠且未对其根据规则2和规则3拓展,f[i]=2表示面i需要被折叠且已经被拓展。

        实现规则2,需要用一个二位数组e[1024][14]记录每个面的每条边连接到的面。

        找到所有将要被折叠的面之后,再根据层级关系依次套多变形切割模板,将旧面形状更新并往面数组追加计算新产生的面,同时维护好数组e。

        给一份测试数据

        更多繁琐细节,参见AC代码。

AC代码

#include <iostream>
#include <cstring>
using namespace std;

#define eps 1e-10
struct Point {
    double x, y;
    Point(double x = 0., double y = 0.): x(x), y(y) {}
};
typedef Point Vector;

Vector operator+ (const Vector& A, const Vector& B) {
    return Vector(A.x + B.x, A.y + B.y);
}

Vector operator- (const Vector& A, const Vector& B) {
    return Vector(A.x - B.x, A.y - B.y);
}

Vector operator* (const Vector& A, double p) {
    return Vector(A.x * p, A.y * p);
}

int dcmp(double x) {
    return abs(x) < eps ? 0 : (x < 0. ? -1 : 1);
}

double Dot(const Vector& A, const Vector& B) {
    return A.x * B.x + A.y * B.y;
}

double Cross(const Vector& A, const Vector& B) {
    return A.x * B.y - A.y * B.x;
}

bool SegmentProperIntersect(const Point& a1, const Point& a2, const Point& b1, const Point& b2) {
    double c1 = Cross(a2 - a1, b1 - a1), c2 = Cross(a2 - a1, b2 - a1);
    double c3 = Cross(b2 - b1, a1 - b1), c4 = Cross(b2 - b1, a2 - b1);
    return dcmp(c1) * dcmp(c2) < 0 && dcmp(c3) * dcmp(c4) < 0;
}

Point GetLineIntersection(const Point& P, const Vector& v, const Point& Q, const Vector& w) {
    Vector u = P - Q;
    return P + v * (Cross(w, u) / Cross(v, w));
}

bool PointInPolygon(const Point& p, const Point* poly, int n) {
    int wn = 0;
    for (int i=0; i<n; ++i) {
        int k = dcmp(Cross(poly[i+1]-poly[i], p-poly[i]));
        int d1 = dcmp(poly[i].y - p.y);
        int d2 = dcmp(poly[i+1].y - p.y);
        if (k > 0 && d1 <= 0 && d2 > 0) ++wn;
        if (k < 0 && d2 <= 0 && d1 > 0) --wn;
    }
    return wn;
}

bool overlap(const Point* p, int m, const Point* q, int n) {
    for (int i=0; i<m; ++i) for (int j=0; j<n; ++j)
        if (SegmentProperIntersect(p[i], p[i+1], q[j], q[j+1])) return true;
    for (int i=0; i<m; ++i) if (PointInPolygon(p[i], q, n)) return true;
    for (int i=0; i<n; ++i) if (PointInPolygon(q[i], p, m)) return true;
    return false;
}

Point p[1030][15], a, b, s; Vector v; int e[1030][15], c[1030], f[1030], m, n;

int find() {
    for (int i=m-1; i>=0; --i) if (PointInPolygon(a, p[i], c[i])) return i;
}

void rule(int i) {
    f[i] = 2; c[m] = 0;
    for (int j=0; j<c[i]; ++j) {
        int d = dcmp(Cross(v, p[i][j]-s));
        if (d >= 0) {
            int d2 = dcmp(Cross(v, p[i][j+1]-s));
            if (e[i][j] >= 0 && !f[e[i][j]] && (d > 0 || d2 > 0)) f[e[i][j]] = 1;
            p[m][c[m]++] = p[i][j];
            if (d > 0 && d2 < 0) p[m][c[m]++] = GetLineIntersection(p[i][j], p[i][j+1]-p[i][j], s, v);
        } else if (dcmp(Cross(v, p[i][j+1]-s)) > 0) {
            if (e[i][j] >= 0 && !f[e[i][j]]) f[e[i][j]] = 1;
            p[m][c[m]++] = GetLineIntersection(p[i][j], p[i][j+1]-p[i][j], s, v);
        }
    }
    p[m][c[m]] = p[m][0];
    for (int j=i+1; j<m; ++j) if (!f[j] && overlap(p[m], c[m], p[j], c[j])) f[j] = 1;
    for (int j=0; j<m; ++j) if (f[j] == 1) rule(j);
}

Point GetProjection(const Point& p) {
    return s + v * (Dot(v, p-s) / Dot(v, v));
}

int solve() {
    p[0][0] = p[0][4] = {0., 0.}; p[0][1] = {100., 0.}; p[0][2] = {100., 100.}; p[0][3] = {0., 100.};
    m = 1; c[0] = 4; e[0][0] = e[0][1] = e[0][2] = e[0][3] = -1;
    while (n--) {
        cin >> a.x >> a.y >> b.x >> b.y; v.x = a.y-b.y; v.y = b.x-a.x; s.x = (a.x+b.x)/2.; s.y = (a.y+b.y)/2.;
        memset(f, 0, sizeof(f)); rule(find());
        for (int i=m-1, t=0; i>=0; --i) if (f[i]) f[i] = m + t++;
        for (int i=m-1; i>=0; --i) if (f[i]) {
            c[m] = 0;
            for (int j=c[i]; j>0; --j) {
                int d = dcmp(Cross(v, p[i][j]-s));
                if (d >= 0) {
                    b = GetProjection(p[i][j]); p[m][c[m]++] = Point(2*b.x-p[i][j].x, 2*b.y-p[i][j].y);
                    int d2 = dcmp(Cross(v, p[i][j-1]-s));
                    if (e[i][j-1] >= 0 && (d > 0 || d2 > 0)) e[m][c[m]-1] = f[e[i][j-1]];
                    else if (d == 0 && d2 < 0) e[m][c[m]-1] = i;
                    else e[m][c[m]-1] = -1;
                    if (d > 0 && d2 < 0)
                        e[m][c[m]] = i, p[m][c[m]++] = GetLineIntersection(p[i][j], p[i][j-1]-p[i][j], s, v);
                } else if (dcmp(Cross(v, p[i][j-1]-s)) > 0) {
                    e[m][c[m]] = e[i][j-1] >= 0 ? f[e[i][j-1]] : -1;
                    p[m][c[m]++] = GetLineIntersection(p[i][j], p[i][j-1]-p[i][j], s, v);
                }
            }
            p[m][c[m]] = p[m][0]; c[++m] = c[i]; c[i] = 0; p[m][c[m]] = p[i][0];
            for (int j=0; j<c[m]; ++j) p[m][j] = p[i][j], e[m][j] = e[i][j];
            for (int j=0; j<c[m]; ++j) {
                int d = dcmp(Cross(v, p[m][j]-s));
                if (d <= 0) {
                    e[i][c[i]] = e[m][j]; p[i][c[i]++] = p[m][j];
                    int d2 = dcmp(Cross(v, p[m][j+1]-s));
                    if (d == 0 && d2 > 0) e[i][c[i]-1] = f[i];
                    if (d < 0 && d2 > 0)
                        e[i][c[i]] = f[i], p[i][c[i]++] = GetLineIntersection(p[m][j], p[m][j+1]-p[m][j], s, v);
                } else if (dcmp(Cross(v, p[m][j+1]-s)) < 0)
                    e[i][c[i]] = e[m][j], p[i][c[i]++] = GetLineIntersection(p[m][j], p[m][j+1]-p[m][j], s, v);
            }
            p[i][c[i]] = p[i][0];
        }
    }
    cin >> a.x >> a.y;
    int ans = 0;
    for (int i=0; i<m; ++i) if (PointInPolygon(a, p[i], c[i])) ++ans;
    return ans;
}

int main() {
    while (cin >> n && n) cout << solve() << endl;
    return 0;
}
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值