1 条题解
-
0
题意: 有一块凸多边形面包,它有n条边,现在你可以把它泡进牛奶k次,牛奶高度为h,询问k次后有多少面积被牛奶浸泡。
分析: 毒瘤题,卡时间卡的很死,稍微不注意细节就超时了。现在总结一下TLE一整天的经验。 首先如果已经知道哪些边要被泡进牛奶然后求面积是很简单的,直接让那些边推进h长度然后求半平面交的面积,最后拿总面积一减就是牛奶浸泡面积。由于不知道哪k条边要被浸泡,很自然想到可以dfs出来所有组合情况,于是随手写了个dfs,很自然地TLE了。
优化部分:首先dfs可以进行优化,最好想的剪枝就是如果到当前位置时后面的边选不到k条那就直接退出。还可以想到如果牛奶浸泡面积已经等于面包面积了,之后也就不需要选边了,答案就是面包面积。dfs部分优化完了,之后是求半平面交的优化,在求各边半平面交时需要先对各边排序+去重,正常情况下每次dfs到终点都需要进行一次求半平面交,也就是会进行巨多次对边的排序+去重,实际上给出面包形状后各边的倾斜角已经固定,只需要最开始排序一次就够了,另外面包是凸多边形,去重根本去不掉任何边,所以不需要去重。还有一个优化是预处理出每条边的法向量,因为在dfs中将各边推进时每次都要用到这个法向量,如果现场求的话太浪费时间,预处理出来每次直接调用就行。最后一个优化是n = k的情况,这时候就不用dfs了,直接所有边都泡就行。
如果wa了可能是没考虑k > n的情况,此时dfs跑不到终点,没法正确更新答案,直接让k = min(n, k)。
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <cmath> #include <queue> using namespace std; const double eps = 1e-8; const double inf = 1e20; const double pi = acos(-1.0); const int maxp = 25; int sgn(double x) { if(fabs(x) < eps)return 0; if(x < 0)return -1; else return 1; } struct Point { double x, y; Point(){} Point(double _x,double _y){x = _x, y = _y;} void input(){scanf("%lf%lf",&x,&y);} void output(){printf("%.2f %.2f\n",x,y);} bool operator == (Point b)const{return sgn(x-b.x) == 0 && sgn(y-b.y) == 0;} bool operator < (Point b)const{return sgn(x-b.x)== 0?sgn(y-b.y)<0:x<b.x;} Point operator -(const Point &b)const{return Point(x-b.x,y-b.y);} //叉积 double operator ^(const Point &b)const{return x*b.y - y*b.x;} //点积 double operator *(const Point &b)const{return x*b.x + y*b.y;} //返回长度 double len(){return sqrt(x*x+y*y);/*库函数*/} //返回长度的平方 double len2(){return x*x + y*y;} //返回两点的距离 double distance(Point p){return hypot(x-p.x,y-p.y);} Point operator +(const Point &b)const{return Point(x+b.x,y+b.y);} Point operator *(const double &k)const{return Point(x*k,y*k);} Point operator /(const double &k)const{return Point(x/k,y/k);} }; struct Line { Point s,e; Line(){} Line(Point _s,Point _e){s = _s, e = _e;} bool operator ==(Line v){return (s == v.s)&&(e == v.e);} //`根据一个点和倾斜角angle确定直线,0<=angle<pi` Line(Point p,double angle) { s = p; if(sgn(angle-pi/2) == 0){e = (s + Point(0,1));} else{e = (s + Point(1,tan(angle)));} } void input() { s.input(); e.input(); } bool parallel(Line v){return sgn((e-s)^(v.e-v.s)) == 0;/*两向量叉积为0*/ } Point crosspoint(Line v) { double a1 = (v.e-v.s)^(s-v.s); double a2 = (v.e-v.s)^(e-v.s); return Point((s.x*a2-e.x*a1)/(a2-a1),(s.y*a2-e.y*a1)/(a2-a1)); } }; struct halfplane:public Line { double angle; halfplane(){} //`表示向量s->e逆时针(左侧)的半平面` halfplane(Point _s,Point _e) { s = _s; e = _e; } halfplane(Line v) { s = v.s; e = v.e; } void calcangle(){angle = atan2(e.y-s.y,e.x-s.x);} bool operator <(const halfplane &b)const{return angle < b.angle;} }; struct polygon { int n; Point p[maxp]; Line l[maxp]; double getarea() { double sum = 0; for(int i = 0;i < n;i++){ sum += (p[i]^p[i+1]); } return fabs(sum)/2; } }; struct halfplanes { int n;//需要输入 halfplane hp[maxp];//需要输入,且封闭区域都在向量逆时针方向 Point p[maxp]; int que[maxp]; int st,ed;//队列的头尾指针,且下标从0开始,指向元素就是头和尾 void push(halfplane tmp){hp[n++] = tmp;} //去重 void unique() { int m = 1; for(int i = 1;i < n;i++) { if(sgn(hp[i].angle-hp[i-1].angle) != 0) hp[m++] = hp[i]; //去除极角相同的情况下,位置在右边(沿向量方向)的边 else if(sgn( (hp[m-1].e-hp[m-1].s)^(hp[i].s-hp[m-1].s) ) > 0) hp[m-1] = hp[i]; } n = m; } bool halfplaneinsert()//判断半平面交是否存在 { que[st=0] = 0; que[ed=1] = 1; p[1] = hp[0].crosspoint(hp[1]); for(int i = 2;i < n;i++){ while(st<ed && sgn((hp[i].e-hp[i].s)^(p[ed]-hp[i].s))<0)ed--; while(st<ed && sgn((hp[i].e-hp[i].s)^(p[st+1]-hp[i].s))<0)st++; que[++ed] = i; if(hp[i].parallel(hp[que[ed-1]]))return false; p[ed]=hp[i].crosspoint(hp[que[ed-1]]); } while(st<ed && sgn((hp[que[st]].e-hp[que[st]].s)^(p[ed]-hp[que[st]].s))<0)ed--; while(st<ed && sgn((hp[que[ed]].e-hp[que[ed]].s)^(p[st+1]-hp[que[ed]].s))<0)st++; if(st+1>=ed)return false;//最后剩下小于三条直线,表明半平面交不存在 return true; } void getconvex(polygon &con) { p[st] = hp[que[st]].crosspoint(hp[que[ed]]); con.n = ed-st+1; for(int j = st,i = 0;j <= ed;i++,j++) con.p[i] = p[j]; con.p[con.n] = con.p[0]; } }; int n, k, h; Point p[maxp]; halfplanes a, cpy; double ans; int st[maxp], top; Point vec[maxp];//预处理法向量 void dfs(int now, int num)//到当前位置处已经找了num个位置 { if(k-num > n-now || sgn(ans) == 0) return;//剪枝 if(num == k) { for(int i = 1; i <= top; i++) { int t = st[i]; cpy.hp[t].s = cpy.hp[t].s+vec[t]*h; cpy.hp[t].e = cpy.hp[t].e+vec[t]*h; } if(cpy.halfplaneinsert()) { polygon b; cpy.getconvex(b); ans = min(ans, b.getarea()); } else ans = 0.0; cpy = a; return; } dfs(now+1, num); st[++top] = now; dfs(now+1, num+1); top--; } signed main() { while(~scanf("%d%d%d", &n, &k, &h)) { if(n == 0 && k == 0 && h == 0) break; ans = 1e20; for(int i = 0; i < n; i++) p[i].input(); p[n] = p[0]; a.n = 0; for(int i = 0; i < n; i++) a.push(halfplane(p[i], p[i+1])); for(int i = 0;i < n;i++) a.hp[i].calcangle(); sort(a.hp,a.hp+n);//先对倾斜角排序 cpy = a; k = min(n, k);//很重要的处理 for(int i = 0; i < n; i++)//预处理每边的法向量 { vec[i] = a.hp[i].e-a.hp[i].s; vec[i] = Point(-vec[i].y, vec[i].x); vec[i] = vec[i]/vec[i].len(); } if(k < n)//这时候才需要dfs选边 { top = 0; dfs(0, 0); } else { for(int i = 0; i < n; i++) { cpy.hp[i].s = cpy.hp[i].s+vec[i]*h; cpy.hp[i].e = cpy.hp[i].e+vec[i]*h; } cpy.halfplaneinsert(); polygon con; cpy.getconvex(con); ans = con.getarea(); } a.halfplaneinsert(); polygon con; a.getconvex(con); printf("%.2f\n", con.getarea()-ans); } return 0; }
- 1
信息
- ID
- 272
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 11
- 已通过
- 1
- 上传者