HDU 3021题解

因为树的收益超过了木桩消耗的三倍,所以如果有树能够被围住,那么把这个树围住收益肯定最优。
所以先来一次凸包求出木桩最大维护多边形。然后枚举树,把在多边形内的树添加到inS里面,接下来就是找到包围inS集合里面所有树的最小环了。我们发现规定方向就可以用floyd找到最小环,我在这里规定逆时针方向为正,在原来木桩的点的集合,任取两个点出来,如果i -> j边在inS里面所有点的右边,或者点在边上,就连一条有向边。

//
//  Created by Matrix on 2015-12-21
//  Copyright (c) 2015 Matrix. All rights reserved.
//
//
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <sstream>
#include <set>
#include <vector>
#include <stack>
#define ALL(x) x.begin(), x.end()
#define INS(x) inserter(x, x,begin())
#define ll long long
#define CLR(x) memset(x, 0, sizeof x)
using namespace std;
const int inf = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int maxn = 2e2 + 10;
const int maxv = 1e3 + 10;
const double eps = 1e-9;

int comp(double x) {
    if(abs(x) < eps) return 0;
    return x < 0 ? -1 : 1;
}
struct Point {
    double x, y;
    Point(double _x = 0, double _y = 0) : x(_x), y(_y){}
    void read() {
        scanf("%lf%lf", &x, &y);
    }
    Point operator+ (const Point& rhs) {
        return Point(x + rhs.x, y + rhs.y);
    }
    Point operator- (const Point& rhs) {
        return Point(x - rhs.x, y - rhs.y);
    }
    Point operator* (const double& rhs) {
        return Point(x * rhs, y * rhs);
    }
    Point operator/ (const double& rhs) {
        return Point(x / rhs, y / rhs);
    }
    double operator^ (const Point& rhs) {
        return x * rhs.y - y * rhs.x;
    }
    double operator* (const Point& rhs) {
        return x * rhs.x + y * rhs.y;
    }
    bool operator< (const Point& rhs) const {
        if(comp(x - rhs.x) == 0) return comp(y - rhs.y) < 0;
        return comp(x - rhs.x) < 0;
    }
    bool operator== (const Point& rhs) {
        return comp(x - rhs.x) == 0 && comp(y - rhs.y) == 0;
    }
    void print() {
        printf("%f %f\n", x, y);
    }
};
typedef Point Vector;
vector <Point> Gh(vector <Point>& a) {
    vector <Point> res;
    int m = 0;
    sort(ALL(a));
    if(a.size() <= 2) {
        for(int i = 0; i < a.size(); i++) {
            res.push_back(a[i]);
        }
        res.push_back(a[0]);
    }
    for(int i = 0; i < a.size(); i++) {
        while(m > 1 && comp((res[m-1] - res[m-2]) ^ (a[i] - res[m-2])) <= 0) {
            m--;
            res.pop_back();
        }
        m++;
        res.push_back(a[i]);
    }
    int k = m;
    for(int i = a.size() - 2; i >= 0; i--) {
        while(m > k && comp((res[m-1] - res[m-2]) ^ (a[i] - res[m-2])) <= 0) {
            m--;
            res.pop_back();
        }
        m++;
        res.push_back(a[i]);
    }
//  res.pop_back();
//  for(int i = 0; i < res.size(); i++) {
//      res[i].print();
//  }
//  printf("m = %d\n", res.size());
//  puts("\n\n\n");
    return res;
}
bool inPoly(Point p, vector <Point>& poly) { //可在边上
    double area1 = 0, area2 = 0;
    for(int i = 1; i < poly.size(); i++) {
        area1 += abs((poly[i] - p) ^ (poly[i-1] - p));
    }
    for(int i = 2; i < poly.size(); i++) {
        area2 += abs((poly[i] - poly[0]) ^ (poly[i-1] - poly[0]));
    }
    return comp(area1 - area2) == 0;
}
bool PolyOnLeft(Point p, Point q, vector <Point>& poly) {
    Vector v = q - p;
    for(int i = 0; i < poly.size(); i++) {
        if(comp(v ^ (poly[i] - p)) < 0) return false;
    }
    return true;
}
int n, m;
int mp[maxn][maxn];
int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
    while(scanf("%d%d", &n, &m) != EOF) {
        Point t; memset(mp, 0x3f, sizeof mp);
        vector <Point> a, b;
        for(int i = 0; i < n; i++) {
            t.read();
            a.push_back(t);
        }
        for(int i = 0; i < m; i++) {
            t.read();
            b.push_back(t);
        }
        vector <Point> z = Gh(b);//树桩凸包
        vector <Point> inS;
        for(int i = 0; i < a.size(); i++) {
            if(inPoly(a[i], z))
                inS.push_back(a[i]);
        }
        if(!inS.size()) {
            puts("0");
            continue;
        }
        for(int i = 0; i < b.size(); i++) {
            for(int j = 0; j < b.size(); j++) {
                if(b[i] == b[j]) continue;
                if(!PolyOnLeft(b[i], b[j], inS)) continue;
                mp[i][j] = 1;
            }
        }
        for(int k = 0; k < b.size(); k++) {
            for(int i = 0; i < b.size(); i++) {
                for(int j = 0; j < b.size(); j++) {
                    mp[i][j] = min(mp[i][j], mp[i][k] + mp[k][j]);
                }
            }
        }
        int r = inf;
        for(int i = 0; i < b.size(); i++) {
            r = min(r, mp[i][i]);
        }
        int ans = inS.size() * 173 - r * 47;
        printf("%d\n", ans);
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值