poj 2653

题目概述

在坐标系中扔N根粗细不计的木棒,每根木棒的序号为其被扔的序数,若两木棒相交,则后扔的会被视为“在顶端”,先扔的会被压在下面,给出每根木棒端点坐标,求最后有哪些木棒“在顶端”

时限

3000ms/9000ms

输入

每组数据第一行正整数N,其后N行,每行4个浮点数,为木棒端点坐标,输入以N=0结束

限制

1<=N<=100000;1<=“在顶端”木棒数<=1000

输出

每行开头为字符串
Top sticks:
其后若干个数,为“在顶端”的木棒序号,两个数之间以英文逗号和空格分隔,最后输出一个英文句号

样例输入

5
1 1 4 2
2 3 3 1
1 -2.0 8 4
1 4 8 2
3 3 6 -2.0
3
0 0 1 1
1 0 2 1
2 0 3 1
0

样例输出

Top sticks: 2, 4, 5.
Top sticks: 1, 2, 3.

讨论

计算几何,求线段相交,题充其量算模版题(早背过模版了),只是输入量巨大,想快些并不容易,看过几篇题解,通过灵活(但略复杂)运用一个数组的前几个空白位置反复记录,可以将用时降低到400-600ms,额退而求其次,开个链表,效果也说得过去,暴力的平方级方法也试过,然而3秒根本不可能够
为何是“在顶端”呢?因为真正在顶端的应该是摞的最高的那个,而且只有一个,因而加了引号

题解状态

3708K,610MS,G++,1936B

题解代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<list>
using namespace std;
#define INF 0x3f3f3f3f  
#define MAXN 100003
#define memset0(a) memset(a,0,sizeof(a))
#define EPS 1e-6

list<int>marked;//存放“在顶端”的木棒序号
list<int>::iterator it, last;//放到外面也是为了省点常数 last用于指向最后一个元素
double x[MAXN], y[MAXN], x2[MAXN], y2[MAXN];//木棒端点坐标值
inline double xp(double x1, double y1, double x2, double y2, double x3, double y3)
{
    return (x1 - x2)*(y3 - y2) - (y1 - y2)*(x3 - x2);
}
inline bool onsegment(double x, double y, double x1, double y1, double x2, double y2)
{
    return min(x1, x2) <= x&&x <= max(x1, x2) && min(y1, y2) <= y&&y <= max(y1, y2);
}
inline bool intersect(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
{
    double xp1 = xp(x3, y3, x1, y1, x2, y2);
    double xp2 = xp(x4, y4, x1, y1, x2, y2);
    double xp3 = xp(x1, y1, x3, y3, x4, y4);
    double xp4 = xp(x2, y2, x3, y3, x4, y4);
    if (xp1*xp2 < 0 && xp3*xp4 < 0)
        return 1;
    else if (abs(xp1) < EPS&&onsegment(x3, y3, x1, y1, x2, y2))
        return 1;
    else if (abs(xp2) < EPS&&onsegment(x4, y4, x1, y1, x2, y2))
        return 1;
    else if (abs(xp3) < EPS&&onsegment(x1, y1, x3, y3, x4, y4))
        return 1;
    else if (abs(xp4) < EPS&&onsegment(x2, y2, x3, y3, x4, y4))
        return 1;
    return 0;
}//上面三个函数已经背的很熟练了 今天做了好几个题 也懒得解释了
void fun(int N)
{
    for (int p = 0; p < N; p++) {
        scanf("%lf%lf%lf%lf", &x[p], &y[p], &x2[p], &y2[p]);//input
        marked.push_back(p);
        last = marked.end();
        --last;//链表没法随机存储 只能这样
        for (it = marked.begin(); it != last; ) {
            int a = *last, b = *it;//也是为了减小常数
            if (intersect(x[a], y[a], x2[a], y2[a], x[b], y[b], x2[b], y2[b]))
                marked.erase(it++);//链表节点删除后迭代器也会作废 因而利用后缀++先行将下一节点算出再将旧的删除掉
            else
                ++it;//没删还是照常递增 注意前缀++比后缀要快……一点点
        }
    }
    bool f = 0;//控制逗号空格输出
    printf("Top sticks: ");//output
    for (it = marked.begin(); it != marked.end(); ++it) {
        if (f)
            printf(", ");
        printf("%d", *it + 1);//output
        f = 1;
    }
    printf(".\n");//output
}
int main(void)
{
    //freopen("vs_cin.txt", "r", stdin);
    //freopen("vs_cout.txt", "w", stdout);

    int N;
    while (~scanf("%d", &N) && N) {//input
        fun(N);
        marked.clear();//链表里有东西 需要清空
    }
}

EOF

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值