【2015集训队互测】文学(区间DP)(计算几何)

传送门


题解:

一个非常巧妙的DP,可以不能保证在枚举最优解的子集的情况下,一定构造出最优解,但是可以保证在所有情况中一定会算到最优解。

首先对于能够一个半平面覆盖完的特殊处理一下。

否则,解里面至少有两个半平面,首先枚举这两个半平面,剩下的是一个凸的无穷区域里面的点,以这两个半平面交点为中心,对未覆盖的点进行极角排序,枚举剩下的半平面,每个半平面会覆盖一些点,在极角序上形成了若干区间,记录 w [ l ] [ r ] w[l][r] w[l][r] 为覆盖 [ l , r ] [l,r] [l,r] 的点需要的最少花费,每个半平面覆盖的所有区间 全部独立考虑,然后进行DP算一下覆盖的权值即可。

考虑为什么它一定会考虑到所有合法解。

我们从选择了的半平面的补集的交来考虑。

1) 交不存在,这时候选择了两个半平面,会有第三个半平面将所有未覆盖的点覆盖,显然被考虑了。

2)存在交,在交中任取一点,对点集进行极角排序,不难发现解当中的每一条直线覆盖的都是一个连续的极角序区间。

复杂度 O ( n 4 ) O(n^4) O(n4)


代码:

#include
#define ll long long
#define re register
#define db double
#define cs const

using std::cerr;
using std::cout;

struct Pnt{
	db x,y;Pnt(){}Pnt(db _x,db _y):x(_x),y(_y){}void get(){scanf("%lf%lf",&x,&y);}
	friend Pnt operator-(cs Pnt &a,cs Pnt &b){return Pnt(a.x-b.x,a.y-b.y);}
	friend db operator*(cs Pnt &a,cs Pnt &b){return a.x*b.y-a.y*b.x;}
};

struct hp{
	db a,b,c;int w;
	void get(){
		scanf("%lf%lf%lf%d",&a,&b,&c,&w);
	}bool cov(cs Pnt &p){
		return p.x*a+p.y*b<=c;
	}
};

Pnt inter(cs hp &_1,cs hp &_2){
	db div=_1.a*_2.b-_1.b*_2.a;
	return Pnt((_1.c*_2.b-_2.c*_1.b)/div,(_1.c*_2.a-_2.c*_1.a)/-div);
} 

cs int N=1e2+7;

int n,m,ans=1e9;
Pnt p[N],t[N];
hp pl[N];

int f[N],w[N][N];

void Main(){
	scanf("%d%d",&n,&m);
	for(int re i=1;i<=n;++i)pl[i].get();
	for(int re i=1;i<=m;++i)p[i].get();
	for(int re i=1;i<=n;++i){
		bool fl=true;
		for(int re j=1;j<=m&&fl;++j)
			fl=pl[i].cov(p[j]);
		if(fl)ans=std::min(ans,pl[i].w);
	}for(int re i=1;i<=n;++i)
		for(int re j=1;j<i;++j){
			int tl=0;Pnt O=inter(pl[i],pl[j]);
			for(int re k=1;k<=m;++k)
				if(!pl[i].cov(p[k])&&!pl[j].cov(p[k]))
					t[++tl]=p[k];
			std::sort(t+1,t+tl+1,
				[&O](cs Pnt &a,cs Pnt &b){return (a-O)*(b-O)<0;});
			memset(f,0x3f,sizeof f);memset(w,0x3f,sizeof w);
			for(int re k=1;k<=n;++k)if(k!=i&&k!=j)
				for(int re l=1,r;l<=tl;++l){
					if(!pl[k].cov(t[l]))continue;
					for(r=l;r<tl&&pl[k].cov(t[r+1]);++r);
					w[l][r]=std::min(w[l][r],pl[k].w);l=r+1;
				}
			for(int re d=tl;d>1;--d)
				for(int re l=1,r=d;r<=tl;++l,++r){ 
					w[l][r-1]=std::min(w[l][r-1],w[l][r]);
					w[l+1][r]=std::min(w[l+1][r],w[l][r]);
				}
			f[0]=0;
			for(int re k=1;k<=tl;++k)
				for(int re l=0;l<k;++l)
					f[k]=std::min(f[k],f[l]+w[l+1][k]);
			ans=std::min(ans,f[tl]+pl[i].w+pl[j].w);
		}
	cout<<(ans==1e9?-1:ans)<<"\n";
}

inline void file(){
#ifdef zxyoi
	freopen("literatrue.in","r",stdin);
#endif
}signed main(){file();Main();return 0;}

你可能感兴趣的:(区间DP)