bzoj3822 文学 动态规划

       题目等价于平面上一些点,然后给定某些半平面,选某一个半平面有代价,求最小的代价使得选出的半平面的并包含所有给定的点。

       同样,也等价于另一些半平面,其中每个半平面都和题中给出的互补,然后求最小的代价使选出的半平面的交不包含任意一个点。

       特判一个半平面包含所有点的情况;那么剩下的就是求一个凸包不包含任何点,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

你可能感兴趣的:(动态规划,凸包,计算几何,差积)