bzoj1390 [Ceoi2008]Fence

[Ceoi2008]Fence

Time Limit: 10 Sec Memory Limit: 64 MB

Description

在一个大小为1000*1000的区域中,有n个固定点,m棵tree 。 现在你要建一个围栏来保护tree,建它的费用为你选用的固定点的个数 20和 你没有圈进围栏的tree111. 现在希望这个值越小越好. 3<=N<=100. 1<=M<=100

Input

第一行给出n,m 下面开始n行,给出固定的坐标 下面开始m行,给出tree的坐标

Output

输出最小费用

Sample Input

4 3

800 300

200 200

200 700

600 700

400 300

600 500

800 900

Sample Output

171

HINT




这是道真好的凸包模板题啊。。。 显然3 * 20 < 111 所以直接能包就爆呗。。。 感觉做出这道题就凸包入门了。 首先你要会求凸包。 然后你还有会判断点是否在凸包内。 最后还要会不停的缩小凸包,floyd找最小环。 良心入门题啊(以后应该就是**题了吧qwq)
```c++

include<bits/stdc++.h>

define N 105

define eps 1e-8

using namespace std;
struct point{
double x, y;
inline point operator + (const point &A)const{return (point){x + A.x, y + A.y};}
inline point operator - (const point &A)const{return (point){x - A.x, y - A.y};}
inline point operator * (const point &A)const{return (point){x * A.x, y * A.y};}
inline bool operator < (const point &A)const{return x < A.x;}
}gu[N], tree[N], s[N];
int n, m, tot, ans, psize, dis[N][N];
vector p;

inline double cross(point A, point B){return A.x * B.y - A.y * B.x;}

inline bool onleft(point A, point B, point P){return cross(B - A, P - A) >= -eps;}

inline bool onright(point A, point B, point P){return cross(B - A, P - A) <= eps;}

inline void putit(){
scanf("%d%d", &n, &m); ans += 111 * m;
for(int i = 1; i <= n; ++i) scanf("%lf%lf", &gu[i].x, &gu[i].y);
for(int i = 1; i <= m; ++i) scanf("%lf%lf", &tree[i].x, &tree[i].y);
}

inline void tb(){
sort(gu + 1, gu + n + 1);
s[++tot] = gu[1];
for(int i = 2; i <= n; ++i){
while(tot > 1 && !onleft(s[tot - 1], s[tot], gu[i])) tot--;
s[++tot] = gu[i];
}
for(int i = n - 1; i >= 1; --i){
while(tot > 1 && !onleft(s[tot - 1], s[tot], gu[i])) tot--;
s[++tot] = gu[i];
}
tot--;
}

inline bool check(point A){
if(A.x < gu[1].x || A.x > gu[n].x) return false;
int L = 2, R = tot + 1;
while(L < R){
int mid = (L + R) >> 1;
if(onright(s[1], s[mid], A)) R = mid;
else L = mid + 1;
}
if(L == 2 || L == tot + 1) return false;
return onright(s[L], s[L - 1], A);
}

inline void prepare(){
s[tot + 1] = (point){s[1].x, 10000};
for(int i = 1; i <= m; ++i)
if(check(tree[i])){
p.push_back(tree[i]); ans -= 111;
}
psize = p.size();
}

inline bool search(point A, point B){
for(int i = psize - 1; i >= 0; --i)
if(!onleft(A, B, p[i])) return false;
return true;
}

inline void workk(){
if(!psize){printf("%d", m * 111); return;}
memset(dis, 0x3f, sizeof(dis));
for(int i = 1; i <= n; ++i)
for(int j = i + 1; j <= n; ++j){
if(search(gu[i], gu[j])) dis[i][j] = 1;
if(search(gu[j], gu[i])) dis[j][i] = 1;
}
for(int k = 1; k <= n; ++k)
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
int Ans = 1e9;
for(int i = 1; i <= n; ++i) Ans = min(Ans, dis[i][i]);
ans += Ans * 20;
cout << ans;
}

int main()
{
putit();
tb();
prepare();
workk();
return 0;
}

posted @ 2018-11-13 09:41  沛霖  阅读(193)  评论(0编辑  收藏  举报