1. 程式人生 > >[BZOJ1007][HNOI2008]水平可見直線

[BZOJ1007][HNOI2008]水平可見直線

put n+1 ons std con 凸包 clas ret 細節題

這個題我一開始看的時候有凸包的感覺,無奈對算法掌握不是很熟悉,沒有成功的解決,看了題解才知道這個題竟然如此的水,就是一個凸包的模板。

code:

#include <cstdio>
#include <cmath>
#include <algorithm>

typedef double LL; // mark
const int N = 50000 + 10;
const double eps = 1e-10;

struct line {
    LL k, b;
    line (LL x = 0, LL y = 0) : k(x), b(y) {}
} l[N];
int stl, s[N], p[N];
int n;

LL cross(line a, line b, line c) { // 判斷a與b的交點是否在b與c的交點的左側 
    return (b.b - a.b) * (b.k - c.k) - (a.k - b.k) * (c.b - b.b); // mark
}

bool cmp(int a, int b) {
    if (fabs(l[a].k - l[b].k) < eps) return l[a].b < l[b].b ;
    else return l[a].k < l[b].k;
}

bool check(int a, int b, int c) {
    double res = cross(l[a], l[b], l[c]);
    return fabs(res) <= eps || res <= 0;
}

inline void cvx() { // mark
    std::sort(p+1, p+n+1, cmp);
    for (int i = 1; i <= n; ++i) {
        while (stl) {
            if (fabs(l[p[i]].k - l[s[stl]].k) < eps) stl--;
            else if (stl>1 && check(p[i], s[stl], s[stl-1])) stl--;
            else break;
        }
        s[++stl] = p[i];
    }
    
}

int main() {
    scanf("%d", &n);
    if (n == 1) { puts("1 "); return 0; }
    for (int i = 1; i <= n; ++i) {
        scanf("%lf%lf", &l[i].k, &l[i].b);
        p[i] = i;
    }
    
    cvx();
    std::sort(s+1, s+1+stl);
    for (int i = 1; i <= stl; ++i) printf("%d ", s[i]);
    return 0;
}

這個題前後也是折騰了很久,一開始cross那裏寫反了,gg*1;後來發現沒有用double,gg*2;再後來沒有寫實數比較,gg*3;最後才發現自己沒有處理好斜率相同的情況,gg*4,前前後後交了5次,這才改好了這個題,仔細回想一下,如果是考試的話,我大概就已經gg*inf了,以後對於這種細節題還是要仔細。

以及這個凸包的寫法簡直太好了,無需提前入棧前兩個元素,優美。

[BZOJ1007][HNOI2008]水平可見直線