BZOJ1007: [HNOI2008]水平可見直線

【題目描述】
在xoy直角座標平面上有n條直線L1,L2,…Ln,若在y值爲正無窮大處往下看,能見到Li的某個子線段,則稱Li爲可見的,否則Li爲被覆蓋的.
例如,對於直線:
L1:y=x; L2:y=-x; L3:y=0
則L1和L2是可見的,L3是被覆蓋的.
給出n條直線,表示成y=Ax+B的形式(|A|,|B|<=500000),且n條直線兩兩不重合.求出所有可見的直線.
【輸入說明】
第一行爲N(0 < N < 50000),接下來的N行輸入Ai,Bi
【輸出說明】
從小到大輸出可見直線的編號,兩兩中間用空格隔開,最後一個數字後面也必須有個空格
【樣例輸入】
3

-1 0

1 0

0 0

【樣例輸出】
1 2

【分析】
和做凸包的算法很相似,很明顯我們維護的東西是下凸的,它的斜率是單調遞增的,所以可以開一個棧開維護直線。
具體過程:
①先按斜率從小到大排序
②然後每次比較棧頂兩條直線L1,L2,和當前直線L3:
③如果L1,L2交點不在L2,L3交點之後,則刪除L2(彈出棧頂元素)
重複③直到無滿足的點可刪。

注意:在比較之前要先判斷當前直線是否與棧頂直線平行,若平行應彈棧。

代碼:

#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;

const double eps=1e-8;
struct Line { double k,d; int id; } L[50010],s[50010];

int cmp(Line a, Line b) { return a.k<b.k || (fabs(a.k-b.k)<eps && a.d<b.d); }
//剛開始手賤把求交點橫座標(Get函數)打成(a.d-b.d)/(a.k-b.k),WA了3次= =
double Get(Line a, Line b) { return (b.d-a.d)/(a.k-b.k); }

int Ans[50010];

int main()
{
    int n,top=0;
    scanf("%d",&n);
    for (int i=1; i<=n; i++) scanf("%lf%lf",&L[i].k,&L[i].d),L[i].id=i;
    sort(L+1,L+n+1,cmp);

    for (int i=1; i<=n; i++)
    {
        while (top)
        {
            if(fabs(s[top].k-L[i].k)<eps) { top--; continue; }//平行特判
            if (top>1 && Get(L[i],s[top-1])<=Get(s[top],s[top-1])) top--; else break;
        }
        s[++top]=L[i];
    }
    for(int i=1; i<=top; i++) Ans[s[i].id]=1;
    for(int i=1; i<=n; i++) if (Ans[i]) printf("%d ",i);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章