POJ 1755 Triathlon I

題目鏈接:http://poj.org/problem?id=1755

題意:鐵人三項比賽,給出n個人進行每一項的速度vi, ui, wi;  對每個人判斷,通過改變3項比賽的路程,是否能讓該人獲勝(嚴格獲勝)。

思路:題目實際上是給出了n個式子方程,Ti  = Ai * x + Bi * y + Ci * z , 0 < i < n

          要判斷第i個人能否獲勝,即判斷不等式組   Tj - Ti > 0,      0 < j < n && j != i    有解

        即 (Aj - Ai)* x + (Bj - Bi) * y + ( Cj - Ci ) * z > 0,   0 < j < n && j != i 有解

         由於 z > 0, 所以 可以兩邊同時除以 z, 將 x / z, y / z 分別看成 x和 y , 這樣就化三維爲二維,可用半平面交判斷是否存在解了,

         對每個人構造一次,求一次半平面交即可。

我用的ZZY的 I&S算法的模版,做的過程中要將A*x + B * y + C > 0表示的半平面,轉化成由兩點組成的向量表示, IQ問題,糾結挺久

首先,所有的半平面保證符號一致(我取的> ), 然後根據 A, B, C的正負構造向量,下面隨便畫了個小圖,幫助理解

我取的每個向量的左邊爲半平面, 圖中 d1, d2就是代碼中的,表示的A, B的正負

#include <iostream>  
#include <cstdio>  
#include <algorithm>  
#include <cmath>  
using namespace std;  
  
const double eps = 1e-10;  
const double maxl = 1e10;  
const int maxn = 105;  
  
int dq[maxn], top, bot, pn, order[maxn], n, A[maxn], B[maxn], C[maxn];  
struct Point {  
    double x, y;  
} p[maxn];  
  
struct Line {  
    Point a, b;  
    double angle;  
} tmpL[maxn];  
  
int dblcmp(double k) {  
    if (fabs(k) < eps) return 0;  
    return k > 0 ? 1 : -1;  
}  
  
double multi(Point p0, Point p1, Point p2) {  
    return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);  
}  
  
bool cmp(int u, int v) {  
    int d = dblcmp(tmpL[u].angle-tmpL[v].angle);  
    if (!d) return dblcmp(multi(tmpL[u].a, tmpL[v].a, tmpL[v].b)) > 0;  
    return d < 0;  
}  
  
void getIntersect(Line l1, Line l2, Point& p) {  
    double dot1,dot2;  
    dot1 = multi(l2.a, l1.b, l1.a);  
    dot2 = multi(l1.b, l2.b, l1.a);  
    p.x = (l2.a.x * dot2 + l2.b.x * dot1) / (dot2 + dot1);  
    p.y = (l2.a.y * dot2 + l2.b.y * dot1) / (dot2 + dot1);  
}  
  
  
bool judge(Line l0, Line l1, Line l2) {  
    Point p;  
    getIntersect(l1, l2, p);  
    return dblcmp(multi(p, l0.a, l0.b)) <= 0;  /*此處有=,也就是交點p在 l0 上時, dq中最上面的半平面也去掉,因爲題目要求嚴格獲勝,也就是最終求出的 
                                                                        半平面交爲一個點,也認爲是無法獲勝的 
                                                                     */  
 }  
  
void addLine(double x1, double y1, double x2, double y2, Line& l) {  
    l.a.x = x1; l.a.y = y1;  
    l.b.x = x2; l.b.y = y2;  
    l.angle = atan2(y2-y1, x2-x1);  
}  
  
bool halfPlaneIntersection(Line l[], int n) {  
    int i, j;  
  
    for (i = 0; i < n; i++) order[i] = i;  
    sort(order, order+n, cmp);  
    for (i = 1, j = 0; i < n; i++)  
        if (dblcmp(l[order[i]].angle-l[order[j]].angle) > 0)  
            order[++j] = order[i];  
    n = j + 1;  
    dq[0] = order[0];  
    dq[1] = order[1];  
    bot = 0;  
    top = 1;  
    for (i = 2; i < n; i++) {  
        while (bot < top && judge(l[order[i]], l[dq[top-1]], l[dq[top]])) top--;  
        while (bot < top && judge(l[order[i]], l[dq[bot+1]], l[dq[bot]])) bot++;  
        dq[++top] = order[i];  
    }  
    while (bot < top && judge(l[dq[bot]], l[dq[top-1]], l[dq[top]])) top--;  
    while (bot < top && judge(l[dq[top]], l[dq[bot+1]], l[dq[bot]])) bot++;  
    if (bot + 1 >= top) return false; //當dq中少於等於兩條邊時,說明半平面無交集  
    return true;  
}  
  
void solve() {  
    int i, j, k;  
    double x1, y1, x2, y2, a, b, c;  
     
   //給半平面加一個框,這樣可以使解x,y都大於0,也可以避免所有半平面交起來後爲不爲凸多邊形,而是一個敞開的區域  
  //如果題目輸入的不是一個多邊形,而是本題這種輸入若干不等式組的情況,這樣的限定就是必須的,不然有bug,例如,兩條線是平行的(但是極角不同),  
  //極角排序後又挨在一起, 那麼就可能求它們的交點,就容易出錯  
   addLine(0, 0, maxl, 0, tmpL[0]);  
    addLine(maxl, 0, maxl, maxl, tmpL[1]);  
    addLine(maxl, maxl, 0, maxl, tmpL[2]);  
    addLine(0, maxl, 0, 0, tmpL[3]);  
    for (i = 0; i < n; i++) {  
         bool flag = false;  
         for (k = 4, j = 0; j < n; j++)  
            if (i != j) {  
                a = 1.0 / A[j] - 1.0 / A[i];  
                b = 1.0 / B[j] - 1.0 / B[i];  
                c = 1.0 / C[j] - 1.0 / C[i];  
                int d1 = dblcmp(a);  
                int d2 = dblcmp(b);  
                int d3 = dblcmp(c);  
                /*本人IQ較低,以下這段糾結一個小時。。。 
                  下面是根據a*x+b*y+c>0取向量p1p2, 
                  其中p1(x1,y1),p2(x2,y2) 
                  就是將直線轉化爲以兩點的表示,取向量p1p2左半爲半平面 
                */  
                if (!d1) {  
                    if (!d2) {  
                        if (d3 <= 0) {  
                            flag = true; break;  
                        }  
                        continue;  
                    }  
                    x1 = 0;  
                    x2 = d2;//d2的值爲1或-1  
                    y1 = y2 = - c / b;  
                }  
                else {  
                    if (!d2) {  
                        x1 = x2 = - c / a;  
                        y1 = 0;  
                        y2 = -d1;  
                    }  
                    else {  
                        x1 = 0; y1 = - c / b;  
                        x2 = d2;  
                        y2 = -(c + a * x2) / b;  
                    }  
                }  
                addLine(x1, y1, x2, y2, tmpL[k]);  
                k++;  
            }  
         if (flag || !halfPlaneIntersection(tmpL, k)) printf ("No\n");  
         else printf ("Yes\n");  
    }  
}  
int main()  
{  
    scanf ("%d", &n);  
    for (int i = 0; i < n; i++)  
        scanf ("%d%d%d", &A[i], &B[i], &C[i]);  
    solve();  
  
    return 0;  
}  


發佈了88 篇原創文章 · 獲贊 60 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章