题目等价于平面上一些点,然后给定某些半平面,选某一个半平面有代价,求最小的代价使得选出的半平面的并包含所有给定的点。
同样,也等价于另一些半平面,其中每个半平面都和题中给出的互补,然后求最小的代价使选出的半平面的交不包含任意一个点。
特判一个半平面包含所有点的情况;那么剩下的就是求一个凸包不包含任何点,N^2枚举凸包上的一个点作为基点,然后令dp[i]为基点到i不包含任何点的最优解。显然按照极角排序后在凸包上的一个半平面覆盖的必定是一段区间,因此可以dp,预处理w[i][j]表示包含i~j的最小代价即可。
AC代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 1000000000
#define N 105
using namespace std;
int n,m,cnt,a[N],b[N],c[N],d[N],f[N],w[N][N];
struct point{ double x,y; }o,p[N],q[N];
void dn(int &x,int y){ if (x>y) x=y; }
point operator -(point u,point v){
u.x-=v.x; u.y-=v.y; return u;
}
double crs(point u,point v){ return u.x*v.y-u.y*v.x; }
bool cmp(const point &u,const point &v){
return crs(u-o,v-o)<0;
}
bool isd(point t,int k){
return t.x*a[k]+t.y*b[k]<=c[k];
}
int main(){
scanf("%d%d",&n,&m); int i,j,k,l,last,ans=inf;
for (i=1; i<=n; i++)
scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
for (i=1; i<=m; i++) scanf("%lf%lf",&p[i].x,&p[i].y);
for (i=1; i<=n; i++){
for (j=1; j<=m; j++)
if (!isd(p[j],i)) break;
if (j>m) dn(ans,d[i]);
}
double fm,fz;
for (i=2; i<=n; i++)
for (j=1; j<i; j++) if (fm=(double)a[i]*b[j]-(double)a[j]*b[i]){
for (k=1,cnt=0; k<=m; k++)
if (!isd(p[k],i) && !isd(p[k],j)) q[++cnt]=p[k];
if (!cnt){ dn(ans,d[i]+d[j]); continue; }
memset(w,0x3f,sizeof(w)); memset(f,0x3f,sizeof(f));
fz=(double)c[i]*b[j]-(double)c[j]*b[i];
o.x=fz/fm; o.y=(c[i]-o.x*a[i])/b[i];
sort(q+1,q+cnt+1,cmp);
for (k=1; k<=n; k++) if (k!=i && k!=j){
for (l=1,last=0; l<=cnt; l++)
if (isd(q[l],k)){
if (!last) last=l;
} else
if (last){
dn(w[last][l-1],d[k]);
last=0;
}
if (last) dn(w[last][cnt],d[k]);
}
for (k=1; k<=cnt; k++)
for (l=2; l<=k; l++) dn(w[l][k],w[l-1][k]);
f[0]=0;
for (k=1; k<=cnt; k++)
for (l=0; l<k; l++) dn(f[k],f[l]+w[l+1][k]);
dn(ans,f[cnt]+d[i]+d[j]);
}
printf("%d\n",(ans<inf)?ans:-1);
return 0;
}
by lych
2016.5.12