bzoj 3822: 文学 动态规划

来源:互联网 发布:树莓派怎样编程 编辑:程序博客网 时间:2024/06/05 10:21

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

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

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

0 0
原创粉丝点击