传送门
题解:
一个非常巧妙的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<bits/stdc++.h>
#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;}