因为树的收益超过了木桩消耗的三倍,所以如果有树能够被围住,那么把这个树围住收益肯定最优。
所以先来一次凸包求出木桩最大维护多边形。然后枚举树,把在多边形内的树添加到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;
}