ACM_几何] Metal Cutting(POJ1514)半平面割与全排暴力切割方案

简介:

[

Description

In order to build a ship to travel to Eindhoven, The Netherlands, various sheet metal parts have to be cut from rectangular pieces of sheet metal. Each part is a convex polygon with at most 8 vertices. Each rectangular piece of sheet metal has width n and height m, so that the four corners of the sheet can be specified by the Cartesian coordinates (0, 0), (0, m), (n, m) and (n, 0) in clockwise order. The cutting machine available can make only straight-line cuts completely through the metal. That is, it cannot cut halfway through the sheet, turn, and then cut some more. You are asked to write a program to determine the minimum total length of cuts this machine has to make in order to cut out the polygon. The cuts must be along the edges of the poligon. 

For example, if n = m = 100, and the polygon has vertices (80, 80), (70, 30), (20, 20) and (20, 80), the following diagram shows the optimal cut (the thick lines). The numbers show the order in which the cuts are made. 

Input

The first line of input contains the two integers n and m where 0 < n, m <= 500. The next line contains p, the number of vertices in the polygon, where 3 <= p <= 8. Each of the next p lines contains two integers x and y where 0 < x < n and 0 < y < m, specifying the vertices of the polygon. The vertices are listed in clockwise order. You may assume that the polygon does not intersect itself, and that no three consecutive vertices are colinear.

Output

Print the minimum total length of cuts required to cut out the given polygon, accurate to 3 decimal places.

Sample Input

100 100
4
80 80
70 30
20 20
20 80

Sample Output

Minimum total length = 312.575

Source

 

题目大意:给你一个长为m宽为n的木板再给你一个凸的p边形的p个坐标点,求最短切割路径长度(这里割只能一刀子到头,不能停不能弯)。

Wrong啦:这题由于最多为8边形,所以果断采用暴力方法,枚举所有切割次序,注意这里的切割最好用半平面法来做,不然要考虑的情况特别多,其中我刚开始拿到这题就当成普通的几何问题来做,把每个割痕分为3部分l1、l2、l3再利用深搜动态的调整每一个割痕对应的l1/l2/l3的值,同时考虑边界切割问题和l1或l3为0的情况,结果都不能过,最后发现少考虑了一种最坑的情况,即:非常大木板与非常小正六边形问题(这里会产生新割痕对旧割痕的影响,所以不得不换用另一种思路!

  错误代码

半平面法最后想到了半平面的方法,就是把木板看成一个4个顶点的凸包,切出里面的p多边形即依次枚举切割顺序,对于每一次切割肯定沿着某一条边,这样最多8!种情况。然后每一次切割我用点集st[]维护每次切过后剩下的部分(新的凸包),np维护新凸包的顶点数。这里的维护就采用了半平面割的方法:

复制代码
  1 #include <cmath>
  2 #include <cstdio>
  3 #include<algorithm>
  4 using namespace std;
  5 const  int maxn = 100;
  6 
  7 
  8 double min(double a,double b){return a<b?a:b;}//求2个double中较小的一个
  9 
 10 const double eps = 1e-8;
 11 double sgn(double x) {return fabs(x)<eps?0:(x>0?1:-1);}//经典比较2个double类数的方法,相等0,大于1,小于-1
 12 //------------------------------------------------------------------
 13 //2维几何模板
 14 //------------------------------------------------------------------
 15 struct Point{//点类(x,y)构造函数+==重定义
 16     double x,y;
 17     Point(double tx=0,double ty=0){x=tx;y=ty;}
 18     bool operator == (const Point& t) const {
 19         return sgn(x-t.x)==0 && sgn(y-t.y)==0;
 20     }
 21 }p[maxn],Set[maxn],st[maxn],tmp[maxn],pp[maxn];
 22 
 23 double dist(Point a,Point b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}//a、b两点的距离
 24 double cross(Point a,Point b,Point c){return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);}//向量ab和向量ac的差积
 25 
 26 struct Seg{Point s,e;};//射线se
 27 bool outside(Seg seg,Point p){return cross(seg.s,seg.e,p)>eps;}//点p在射线seg左
 28 bool inside(Seg seg,Point p){return cross(seg.s,seg.e,p)<-eps;}//点p在射线seg右
 29 
 30 Point Intersect(Point p1, Point p2, Point p3, Point p4, Point& p) {
 31     double a1, b1, c1, a2, b2, c2, d;
 32     a1 = p1.y - p2.y; b1 = p2.x - p1.x; c1 = p1.x*p2.y - p2.x*p1.y;
 33     a2 = p3.y - p4.y; b2 = p4.x - p3.x; c2 = p3.x*p4.y - p4.x*p3.y;
 34     d = a1*b2 - a2*b1;
 35     if ( fabs(d) < eps )    return false;
 36     p.x = (-c1*b2 + c2*b1) / d;
 37     p.y = (-a1*c2 + a2*c1) / d;
 38     return p;
 39 }//直线p1p2和p3p4的交点存在p中(当且仅当Cross(p1p2,p3p4)非0)
 40 //-----------------------------------------------------------------
 41 double W,H;
 42 int a[10],n,pn;
 43 double CUT(Seg seg,Point p[]){
 44     int i,j,tot=0;
 45     Point A,B;
 46     A=B=Point(0,0);
 47     bool s,e;
 48     for(i=0;i<pn;i++){//这里A、B交替存储seg与动态凸包p[]的交点,并把新凸包保存在pp中,新的凸包点数保存在tot中
 49         if(!outside(seg,p[i]))pp[tot++]=p[i];//p[i]在射线seg右或上
 50         else {
 51             if(i==0&&!outside(seg,p[pn-1])){//当前p[i]p[i-1]和seg的交点(当i==0时要特殊处理一下)
 52                 B=A;
 53                 pp[tot++]=Intersect(seg.s,seg.e,p[i],p[pn-1],A);
 54             }
 55             if(i!=0&&!outside(seg,p[i-1])) {
 56                 B=A;
 57                 pp[tot++]=Intersect(seg.s,seg.e,p[i],p[i-1],A);
 58             }
 59             if(!outside(seg,p[i+1])) {//当前p[i]p[i+1]和seg的交点(因为我们已经令p[最后一个的后一个]=p[0]所以不必特殊处理)
 60                 B=A;
 61                 pp[tot++]=Intersect(seg.s,seg.e,p[i],p[i+1],A);
 62             }
 63         }
 64     }
 65     pp[tot]=pp[0];//特殊处理尾部
 66     pn=tot;memcpy(st,pp,sizeof(pp));//更新p[]和pn
 67     return dist(A,B);//返回割痕长度
 68 }
 69 int main(){
 70     int i;
 71     while(scanf("%lf%lf",&W,&H)!=EOF){
 72         double ans=1e20;
 73         st[4]=st[0]=Point(0,0);//tmp[100]是用来保存原来矩形凸包外四个顶点的,
 74         st[1]=Point(0,H);      //st[100]是切割过程中凸包的顶点
 75         st[2]=Point(W,H);      //因此对于每种切割方案,刚开始都要将st设为原始矩形凸包,这也是tmp存在的原因
 76         st[3]=Point(W,0);
 77         memcpy(tmp,st,sizeof(st));
 78         scanf("%d",&n);Seg ts[100];
 79         for(i=0;i<n;i++) {
 80             scanf("%lf%lf",&p[i].x,&p[i].y);
 81             a[i]=i;//0、1、2.....后面对其全排枚举实现所有切割方法的暴力枚举
 82         }p[n]=p[0];
 83         for(i=0;i<n;i++) ts[i].s=p[i],ts[i].e=p[i+1];
 84         do{
 85             double tlen=0;//切割长度
 86             memcpy(st,tmp,sizeof(tmp));
 87             pn=4;//pn和st[100]一样是计算过程中的量(对于每种切割方案,其开始要更新为原来的,其过程要变化,pn即凸包st的点数)
 88             for(i=0;i<n;i++){//按照获得的全排序列切割
 89                 tlen+=CUT(ts[a[i]],st);
 90             }
 91             ans=min(ans,tlen);//求出最小值,保存在ans里
 92         }while(next_permutation(a,a+n));//暴力枚举所有情况next_permutation(a,a+n)是将数组全排列找出
 93         printf("Minimum total length = %.3lf\n",ans);
 94     }
 95     return 0;
 96 }
 97 /*
 98 线的交点
 99 1>直线可以用直线上一点P0和方向向量v来表示:直线上所有点P满足P=P0+t*v,其中t为参数;如果已知直线上的2个不同的点A、B,则方向向量
100 为B-A,所以参数方程为A+(B-A)*t;参数方程可以方便的表示出直线射线和线段,区别仅在于t的范围:直线t无范围,射线t>0,线段t在0~1之间
101 2>直线交点:设直线分别为P+t*v,Q+t*w,u=PQ,交点在第一条直线上的参数为t1、在第二条直线上的参数为t2,则x、y的坐标可以列出一个方程,
102 截得:t1=cross(w,u)/cross(v,w),t2=cross(v,u)/cross(v,w),前提要确保分母不为0!!
103 */
104 /*
105 在STL中,除了next_permutation外,还有一个函数prev_permutation,两者都是用来计算排列组合的函数。
106 前者是求出下一个排列组合,而后者是求出上一个排列组合。所谓“下一个”和“上一个”,书中举了一个
107 简单的例子:对序列 {a, b, c},每一个元素都比后面的小,按照字典序列,固定a之后,a比bc都小,c比b大,
108 它的下一个序列即为{a, c, b},而{a, c, b}的上一个序列即为{a, b, c},同理可以推出所有的六个序列为:
109 {a, b, c}、{a, c, b}、{b, a, c}、{b, c, a}、{c, a, b}、{c, b, a},其中{a, b, c}没有上一个元素,
110 {c, b, a}没有下一个元素。
111 */
复制代码

 



本文转自beautifulzzzz博客园博客,原文链接:http://www.cnblogs.com/zjutlitao/p/3526893.html,如需转载请自行联系原作者

相关文章
|
4月前
|
机器学习/深度学习 算法 C#
C# | 凸包算法之Andrew‘s,获取围绕一组点的凸多边形的轮廓点
这篇关于凸包算法的文章,本文使用C#和Andrew’s算法来实现凸包算法。 首先消除两个最基本的问题: 什么是凸包呢? 凸包是一个包围一组点的凸多边形。凸多边形是指多边形中的每个内角都小于180度的多边形。 凸包算法有什么用呢? 凸包算法的作用是找到这个凸多边形,并且使用最少的点来绘制出它的轮廓。凸包算法在计算机图形学、计算几何和机器学习等领域中有着广泛的应用。
51 0
|
2月前
|
机器学习/深度学习 算法 数据挖掘
Hybrid-SORT起飞 | 超过DeepSORT将近10个点的多目标跟踪香不香?
Hybrid-SORT起飞 | 超过DeepSORT将近10个点的多目标跟踪香不香?
60 0
|
8月前
|
算法
算法提高:计算几何基础 | 详解凸包问题
点集Q的凸包(convex hull)是指一个最小凸多边形,满足Q中的点或者在多边形边上,或者在其内
83 0
算法提高:计算几何基础 | 详解凸包问题
|
4月前
【每日一题Day246】LC1659最大化网格幸福感 | 状压dp
【每日一题Day246】LC1659最大化网格幸福感 | 状压dp
22 0
|
9月前
|
机器学习/深度学习
UPC - 2022春混合个人训练赛第五场 D Seahorse Shoes(贪心+模拟)
UPC - 2022春混合个人训练赛第五场 D Seahorse Shoes(贪心+模拟)
59 0
|
4月前
|
机器学习/深度学习 算法 C#
C# | 凸包算法之Jarvis,寻找一组点的边界/轮廓
这篇关于凸包算法的文章,本文使用C#和Jarvis算法来实现凸包算法。 首先消除两个最基本的问题: 什么是凸包呢? 凸包是一个包围一组点的凸多边形。凸多边形是指多边形中的每个内角都小于180度的多边形。 凸包算法有什么用呢? 凸包算法的作用是找到这个凸多边形,并且使用最少的点来绘制出它的轮廓。凸包算法在计算机图形学、计算几何和机器学习等领域中有着广泛的应用。
34 0
|
机器学习/深度学习 算法
深度之眼(十二)——svd分解的应用
深度之眼(十二)——svd分解的应用
深度之眼(十二)——svd分解的应用
|
传感器 编解码 算法
LeGO-LOAM算法详解
LeGO-LOAM算法详解
291 0
LeGO-LOAM算法详解
|
算法
【算法竞赛进阶指南】車的放置(行列模型二分图最大匹配+匈牙利算法)
【算法竞赛进阶指南】車的放置(行列模型二分图最大匹配+匈牙利算法)
81 0