题目等价于平面上一些点,然后给定某些半平面,选某一个半平面有代价,求最小的代价使得选出的半平面的并包含所有给定的点。
同样,也等价于另一些半平面,其中每个半平面都和题中给出的互补,然后求最小的代价使选出的半平面的交不包含任意一个点。
特判一个半平面包含所有点的情况;那么剩下的就是求一个凸包不包含任何点,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