hdu6755 Mow

半平面交 模擬 雙端隊列

慢慢啃

人生第一次程式碼過兩百行啊…加油加油

 

未來のために 頑張ってください

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<iomanip>

#define ld long double
using namespace std;
   
const int maxn = 1e3;
const ld pi = acosl(-1);
const ld eps = 1e-10;//控制精度 

int n;
int head, tail;
ld r, cost1, cost2;

typedef struct Grid
{//棋盤 
    ld x,y;
    Grid(double a = 0, double b = 0){x = a, y = b;}//構造函數,結構體對象創建時執行 
} point , Vector ;//point:類型為Grid的數據類型;Vector與point結構相同,數據類型名稱不同 

typedef struct Net
{
    point a,b;
    Net() {}//重載構造函數,目的是以形如line x;的方式定義直線,避免定義時確定參數的複雜性 
    Net(point i, point j){a = i, b = j;}
} line ;

point node[maxn], p[maxn];
line l[maxn], L[maxn], q[maxn];

Vector operator - (point a, point b) // V需大寫,避免與vector混淆 
{
    return Vector(b.x - a.x, b.y - a.y);    
}//矢量定義 

double operator * (Vector a, Vector b)
{
    return (a.x * b.y - a.y * b.x);
}//叉乘 

ld getangle(Vector t)
{
    return atan2(t.y, t.x);//返回一個(-pi,pi]的角度 
}//獲得某個矢量的極角 

ld getangle(line t)
{
    return atan2(t.b.y - t.a.y, t.b.x - t.a.x);
}//獲得某條直線的極角 

bool cmp(line a, line b)
{    
    Vector v1 = (a.b - a.a), v2 = (b.b - b.a);
    ld A = getangle(v1), B = getangle(v2);
    if(fabs(A - B) < eps){
        return (v1 * (b.b - a.a)  >= 0);//靠左邊的丟在後面,方便雙端隊列去重 
    }
    return A < B;
}//決定根據極角排序的直線的優先順序 

bool check()
{
    ld ans = 0;
    for(int i = 1 ; i < n-1 ; i++){
        ans += (node[i] - node[0]) * (node[i+1] - node[0]);
    }
    if(ans < 0)    return false;
    return true;
}//檢查point輸入順序進行調整 true為逆時針 

point getnode(line a, line b)//求兩直線(線段)交點,一般式三參數暴力求解 
{
    ld a1 = a.b.y - a.a.y, b1 = a.a.x - a.b.x, c1 = a.b.x * a.a.y - a.a.x * a.b.y;
    ld a2 = b.b.y - b.a.y, b2 = b.a.x - b.b.x, c2 = b.b.x * b.a.y - b.a.x * b.b.y;
    ld D = a1 * b2 - a2 * b1;
    //Dx/D,Dy/D,注意c1、c2移至右側變號 
    return point((c2 * b1 - b2 * c1) / D, (c1 * a2 - a1 * c2) / D);
}

bool on_right(line a, line b, line c)//觀察a、b交點與c的關係 
{
    point n = getnode(a, b);
    if((c.b - c.a) * (n - c.a) < 0){//交點在右側/下方 
         return true;
    }
    return false;
}

line narrow_line(line x)//實現直線平移,勾畫出圓心軌跡 
{
    ld dx = x.b.x - x.a.x, dy = x.b.y - x.a.y;
    Vector turnv;//旋轉矢量
    turnv.x = -dy, turnv.y = dx;
    
    ld m = sqrtl(dx * dx + dy * dy);    
    turnv.x = turnv.x / m * r;
    turnv.y = turnv.y / m * r;
    //求對應坐標
    x.b.x += turnv.x;
    x.b.y += turnv.y;
    x.a.x += turnv.x;
    x.a.y += turnv.y;
    return x;
}

void narrow_polygon()//模擬圓心軌跡 
{
    for(int i = 0 ; i <= n-1 ; i++){
        L[i] = narrow_line(l[i]);
    }
}
 
bool HPI()
{
    sort(L,L+n,cmp);//按極角排序 
    head = 0, tail = 0;
    int cnt = 0;
    for(int i = 0 ; i < n-1 ; i++){
        Vector v1 = L[i].b - L[i].a, v2 = L[i+1].b - L[i+1].a;//?        
        if(fabs(getangle(v1) - getangle(v2)) < eps){
            continue;
        }
        L[cnt++] = L[i];
    }
    L[cnt++] = L[n-1];
    
    /////正片開始
    for(int i = 0 ; i < cnt ; i++){//注意下標 
        while(tail - head > 1 && on_right(q[tail-1], q[tail-2], L[i]))    tail--;//符合onright規則,刪去上一條直線 
        while(tail - head > 1 && on_right(q[head], q[head+1], L[i]))    head++;//頭部同理 
        q[tail++] = L[i];
    }
    //換個角度看世界,最後檢驗q[head]以及q[tail-1],q[tail]實際為空
    while(tail - head > 1 && on_right(q[tail-1], q[tail-2], q[head]))    tail--;
    while(tail - head > 1 && on_right(q[head], q[head+1], q[tail-1]))    head++;
    
    if((tail - head) >= 3)    return true;
    return false;
}

ld get_S()//求初始面積 
{
    ld ans = 0;
    for(int i = 1 ; i < n ; i++){
        ans += fabs((node[i] - node[0]) * (node[(i+1)%n] - node[0]));
    }
    return ans / 2;
}

ld dis(point a, point b)
{
    ld dx = b.x - a.x, dy = b.y - a.y;
    return sqrtl(dx * dx + dy * dy);
}//兩點間距離 

ld get_inS()//內部半平面交 面積 
{
    ld res = 0;
    int tot = 0;
    for(int i = head ; i < tail-1 ; i++){
        p[tot++] = getnode(q[i], q[i+1]);
    }
    p[tot++] = getnode(q[tail-1], q[head]);
    
    for(int i = 1 ; i < tot-1 ; i++){
        res += fabs((p[i] - p[0]) * (p[i+1] - p[0]));
    }
    res /= 2;
    
    for(int i = 0 ; i < tot-1 ; i++){
        res += dis(p[i], p[i+1]) * r;
    }// c * r
    res += dis(p[0], p[tot - 1]) * r;
    
    return res + r * r * pi;
}

int main(){
    //freopen("mow.in","r",stdin);
    int T;cin >> T;
    while( T-- )
    {
        cin >> n >> r >> cost1 >> cost2;
        for(int i = n-1 ; i >= 0 ; i--){
            cin >> node[i].x >> node[i].y;
        }//本題中為順時針方向,因而逆序存點,以確保直線左側為內側 
        
        if(check()){//
            for(int i = 0 ; i < n-1 ; i++){// 
                l[i] = line(node[i], node[i+1]);
            }
            l[n-1] = line(node[n-1], node[0]);
        }else{//
            for(int i = 0 ; i < n-1 ; i++){
                l[i] = line(node[i+1],node[i]);
            }
            l[n-1] = line(node[0], node[n-1]);
        }//0 ~ n-1
        
        narrow_polygon();
        
        ld s = get_S();//草坪面積 
        ld ans = s * cost1;//手動割草費用
        
        if(HPI()){
            ld mowable = get_inS();//機器割草面積 
            ans = min(ans, mowable * cost2 + (s - mowable) * cost1);
        }
        cout << fixed << setprecision(25) << ans << "\n";
        
    }
    
    return 0;
}

 

//實現直線平移,勾畫出圓心軌跡