bzoj1390 [CEOI2008] Fence
题意
给出n个白点和m个黑点.现在你需要选择一些白点把黑点圈起来.每有一个黑点不能被选出的白点组成的凸包包含就需要付出111的代价,每选出一个白点就需要付出20的代价.要求最小化代价之和
n,m<=100
分析
首先选出三个白点的代价小于扔掉一个黑点的代价,那么显然我们要尽量多地圈黑点.对所有白点求一个凸包,此时包含的黑点数目达到最多,但不一定是最优方案,因为也许可以少选一些白点,包含同样数目的黑点.那么就是选出尽量少的白点使得它们包含之前被凸包包含的黑点.这里我脑抽了一下...实际上推到这里和JSOI 合金 已经等价了,建图跑最小环即可.
因为是计算几何题,所以往CEOI官网扒了数据测了一发过了才敢往bzoj交
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=105;
struct point{
double x,y;
point(){}
point(double a,double b){x=a;y=b;}
void read(){
scanf("%lf%lf",&x,&y);
}
bool operator <(const point &B)const{
return (x==B.x)?y<B.y:x<B.x;
}
bool operator ==(const point &B)const{
return x==B.x&&y==B.y;
}
}P1[maxn],P2[maxn],P3[maxn],stk[maxn];int top=0,cnt=0;
point operator +(const point &A,const point &B){
return point(A.x+B.x,A.y+B.y);
}
point operator -(const point &A,const point &B){
return point(A.x-B.x,A.y-B.y);
}
double cross(const point &A,const point &B){
return A.x*B.y-A.y*B.x;
}
void convexhull(point *P,int n){
sort(P+1,P+n+1);
stk[top++]=P[1];
for(int i=2;i<=n;++i){
while(top>1&&cross(stk[top-1]-stk[top-2],P[i]-stk[top-2])>=0)top--;
stk[top++]=P[i];
}
int lim=top;
for(int i=n-1;i>=1;--i){
while(top>lim&&cross(stk[top-1]-stk[top-2],P[i]-stk[top-2])>=0)top--;
stk[top++]=P[i];
}
--top;
}
bool betw(double x,double a,double b){
return (a<=x&&x<=b)||(b<=x&&x<=a);
}
bool onseg(point o,point A,point B){
return betw(o.x,A.x,B.x)&&cross(A-o,B-o)==0;
}
bool inside(point x,point A,point B,point C){
if(x==A||x==B||x==C)return true;
if(onseg(x,A,B)||onseg(x,A,C)||onseg(x,B,C))return true;
int s1=(cross(B-A,x-A)>0),s2=(cross(C-B,x-B)>0),s3=(cross(A-C,x-C)>0);
return s1==s2&&s2==s3;
}
bool inside(point A){
if(cross(stk[1]-stk[0],A-stk[0])>0)return false;
if(cross(stk[top-1]-stk[0],A-stk[0])<0)return false;
int l=1,r=top-1;
while(l<=r){
int mid=(l+r)>>1;
if(cross(stk[mid]-stk[0],A-stk[0])>0)r=mid-1;
else l=mid+1;
}
return inside(A,stk[0],stk[r],stk[r+1]);
}
int w[105][105];
int main(){
int n,m;scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)P1[i].read();
for(int i=1;i<=m;++i)P2[i].read();
convexhull(P1,n);
for(int i=1;i<=m;++i){
if(inside(P2[i]))P3[++cnt]=P2[i];
}
if(cnt==0)printf("%d\n",111*m);
else{
memset(w,0x3f,sizeof(w));
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
if(i!=j){
bool flag=true;
for(int k=1;k<=cnt;++k){
if(cross(P1[j]-P1[i],P3[k]-P1[i])<0)flag=false;
}
if(flag)w[i][j]=1;
}
}
}
for(int k=1;k<=n;++k){
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
if(w[i][k]+w[k][j]<w[i][j])w[i][j]=w[i][k]+w[k][j];
}
}
}
int ans=0x3f3f3f3f;
for(int i=1;i<=n;++i)if(w[i][i]<ans)ans=w[i][i];
printf("%d\n",20*ans+(m-cnt)*111);
}
return 0;
}