UVa LA 4253 & UVa 1421 Archery 枚举,状态削减,oj错误题目 难度: 1

题目

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4167

 

题意

有n+1条平行于x轴的线段,最底下的线段是[0, w],站在最下面的第0条线段上的某个点上,问有没有一条过该点的直线也通过上面的n条线段。2<=n<=5000,2<=w<=1e7,保证这n+1条线段高度都不同。最高的线段到第0条线段高度差(y坐标差)不超过n,数据保证每条线段高度不同。对于第i条线段,其左右端点横坐标Li,Ri必然满足0<=Li<Ri<=W。第i条线段高度Di满足0<=Di<=W。Li,Ri,Di都是整数,这样精度问题较小。

 

思路

想到直线就会想到两点式或者点斜式。这里两点式一定会TLE。因此只能是点斜式了,枚举斜率并不是一个很好的做法。因此,可以枚举直线上一点,然后通过斜率来看有没有符合结果的直线。由于直线可以平移,因此总可以平移到一种同时经过第i条线段左端点和第k条线段右端点的情况。因此只需要枚举这n+1条线段的左端点。

对于第i条线段的左端点(Li,Di),如果有一条经过(Li,Di)的直线经过全部n+1条线段,则可以输出"YES",否则,还要继续枚举下一线段的左端点。或者可以说,如对(Li,Di),存在斜率k(k可能为无穷)使得对应直线经过全部n+1条线段,则满足题意

由于线段是平行于x轴的,所以不会产生跨x轴的情况,那么就可以维护每条肽段所对应的可行斜率的集合,如果这些集合的并集非空,那么就有符合结果的直线存在。

但是直接维护斜率存在要过90度的情况,因此,可以以(Li,Di)为中心建立极坐标系,维护线段所对应角度。如果比Di高,对应区间为(Ri, Di)对应极角到(Li, Di)对应极角这个封闭区间。如果比Di低,也就是在x轴下方,总可以不失意义转换为x轴上方的对应方向相反的区间。

 

感想:

1. 注意如果把scanf("%d%d", R, &n) == 2作为判断条件,就会wa,这一般是因为测试数据中有冗余

2. 一开始枚举了两条线段的端点<左,左>,<左,右>,<右,左>,<右,右>四种情况,所以一直TLE。但是因为必然可以平移到既有左端点又有右端点,所以直接枚举遍历左或者右端点就够了

3. 精度倒不用特别注意

代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <vector>
#include <map>
#include <set>
using namespace std;
typedef pair<int, int> Pair;
typedef long long ll;
const int MAXN = 5e3 + 4;
const double pie = acos(-1.0);
int n;
int D[MAXN];
int L[MAXN];
int R[MAXN];
bool simplecheck() {
    int mnX = 0;
    int mxX = R[0];
    for (int i = 0; i <= n; i++) {
        mnX = max(mnX, L[i]);
        mxX = min(mxX, R[i]);
    }
    return mnX <= mxX;

}

double getK(int x0, int x1, int y0, int y1) {
    return (y1 - y0) * 1.0 / (x1 - x0);
}

double getAng(int x0, int x1, int y0, int y1) {
    if (x0 == x1)return pie / 2;
    double ang = atan(getK(x0, x1, y0, y1));
    if (ang < 0)return ang + pie;
    return ang;
}

void getAngInterval(int x0, int y0, int i, double& a0, double& a1) {
    if (D[i] == y0) {
        a0 = 0;
        a1 = pie;
    }
    else if (D[i] > y0) {
        a0 = getAng(x0, R[i], y0, D[i]);
        a1 = getAng(x0, L[i], y0, D[i]);
    }
    else {
        a0 = getAng(x0, L[i], y0, D[i]);
        a1 = getAng(x0, R[i], y0, D[i]);
    }
}
int main() {
    int T;
    freopen("C:\\Users\\Iris\\source\\repos\\ACM\\ACM\\input.txt", "r", stdin);
    freopen("C:\\Users\\Iris\\source\\repos\\ACM\\ACM\\output.txt", "w", stdout);
    scanf("%d", &T);
    //cin >> T;
    for (int ti = 1; ti <= T; ti++) {
        scanf("%d%d", R, &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d%d%d", D + i, L + i, R + i);
        }
        bool fl = simplecheck();
        if (!fl) {
            for (int i = 0; i <= n; i++) {
                double amn = 0, amx = pie;
                bool ok = true;
                for (int j = 0; j <= n && ok; j++) {
                    double amnnow, amxnow;
                    getAngInterval(L[i], D[i], j, amnnow, amxnow);
                    if (amnnow > amx) {
                        ok = false;
                        break;
                    }
                    if (amxnow < amn) {
                        ok = false;
                        break;
                    }
                    amn = max(amn, amnnow);
                    amx = min(amx, amxnow); 
                    //printf("From (%d, %d) to (%d, %d)->(%d, %d), ang [%.2f, %.2f], Overall [%.2f, %.2f]\n", L[i], D[i], L[j], D[j], R[j], D[j], amnnow * 180.0 / pie, amxnow * 180.0 / pie, amn * 180.0 / pie, amx * 180.0 / pie);

                }
                if (ok) {
                    fl = true;
                    break;
                }
            }
        }
        if (fl) {
            puts("YES");
        }
        else puts("NO");
    }
    return 0;
}
View Code

 

posted @ 2019-06-09 13:02  雪溯  阅读(222)  评论(0编辑  收藏  举报