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的情况,结果都不能过,最后发现少考虑了一种最坑的情况,即:非常大木板与非常小正六边形问题(这里会产生新割痕对旧割痕的影响,所以不得不换用另一种思路!

 #include<iostream>
#include<math.h>
#include<cstdio>
#include<algorithm>
using namespace std;
class point{
public:
double x,y;
bool in(int n,int m){
return (x>= && n>=x && y>= && m>=y);
}
};
void chose(point a1,point a2,point a3,point a4,point &a5,point &a6,int n,int m){
bool ok=;
if(a1.in(n,m))a5.x=a1.x,a5.y=a1.y,ok=;
if(a2.in(n,m)){
if(ok){a6.x=a2.x;a6.y=a2.y;return;}
else a5.x=a2.x,a5.y=a2.y,ok=;
}
if(a3.in(n,m)){
if(ok){a6.x=a3.x;a6.y=a3.y;return;}
else a5.x=a3.x,a5.y=a3.y,ok=;
}
if(a4.in(n,m))a6.x=a4.x,a6.y=a4.y;
}
double dis(point a,point b){
return sqrt(1.0*(b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y));
}
class line{
public:
double l1,l2,l3;
int num;
bool set(point a,point b,int n,int m){
if(fabs(a.y-b.y)<0.00001){//横着的
if(fabs(a.y-m)<0.00000001 || fabs(a.y)<0.00000001){l1=l3=l2=;return ;}
else if(b.x>a.x){l1=a.x;l2=b.x-a.x;l3=n-b.x;}
else {l1=n-a.x;l2=a.x-b.x;l3=b.x;}
}else if(fabs(a.x-b.x)<0.00001){//竖着的
if(fabs(a.x-n)<0.00000001 || fabs(a.x)<0.00000001){l1=l3=l2=;return ;}
else if(a.y>b.y){l1=m-a.y;l2=a.y-b.y;l3=b.y;}
else {l1=a.y;l2=b.y-a.y;l3=m-b.y;}
}else{//其他情况
l2=sqrt(1.0*(b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y));
double k=(b.y-a.y)/(b.x-a.x);
point PJ[];
PJ[].x=;PJ[].y=a.y-a.x*k;
PJ[].x=a.x-a.y/k;PJ[].y=;
PJ[].x=n;PJ[].y=a.y+(n-a.x)*k;
PJ[].x=a.x+(m-a.y)/k;PJ[].y=m;
/*for(int i=0;i<4;i++)
cout<<PJ[i].x<<' '<<PJ[i].y<<'\n';*/
chose(PJ[],PJ[],PJ[],PJ[],PJ[],PJ[],n,m);
//cout<<PJ[4].x<<' '<<PJ[4].y<<' '<<PJ[5].x<<' '<<PJ[5].y<<'\n';
if(PJ[].x>PJ[].x){
PJ[]=PJ[];
PJ[]=PJ[];
PJ[]=PJ[];
}
if(a.x<b.x){//a在左
l1=dis(PJ[],a);
l3=dis(PJ[],b);
}else{
l1=dis(a,PJ[]);
l3=dis(b,PJ[]);
}
}return ;
} };
bool operator<(line L1,line L2){
return (L1.l1+L1.l3)<(L2.l1+L2.l3);
} //---------------------------------------
line L[];
int p,lose;
int vis[];
double minRoad,proRoad;
void dfs(int c){
bool ok=;
for(int i=;i<p-lose;i++)if(!vis[i]){
int cur=L[i].num;
int pre=(cur+)%p;double prel3;
int next=(cur+)%p;double nextl1; proRoad+=(L[i].l1+L[i].l2+L[i].l3);vis[i]=;
for(int j=;j<p;j++){//减掉L[c]对其前后的影响
if(L[j].num==pre){prel3=L[j].l3;L[j].l3=;}
else if(L[j].num==next){nextl1=L[j].l1;L[j].l1=;}
}
dfs(i);
proRoad-=(L[i].l1+L[i].l2+L[i].l3);vis[i]=;
for(int j=;j<p;j++){//恢复L[c]对其前后的影响
if(L[j].num==pre)L[j].l3=prel3;
else if(L[j].num==next)L[j].l1=nextl1;
}
ok=;
}
if(!ok){//没有要剪得,表明已经剪完
if(proRoad<minRoad)minRoad=proRoad;
}
}
void Solve(){
minRoad=;proRoad=;
for(int i=;i<;i++)vis[i]=;
for(int i=;i<p-lose;i++){
int cur=L[i].num;
int pre=(cur+)%p;double prel3;
int next=(cur+)%p;double nextl1; proRoad+=(L[i].l1+L[i].l2+L[i].l3);vis[i]=;
for(int j=;j<p;j++){//减掉L[c]对其前后的影响
if(L[j].num==pre){prel3=L[j].l3;L[j].l3=;}
else if(L[j].num==next){nextl1=L[j].l1;L[j].l1=;}
}
dfs(i);
proRoad-=(L[i].l1+L[i].l2+L[i].l3);vis[i]=;
for(int j=;j<p;j++){//恢复L[c]对其前后的影响
if(L[j].num==pre)L[j].l3=prel3;
else if(L[j].num==next)L[j].l1=nextl1;
}
}
}
//-------------------------------------
int main(){
int T;cin>>T;int ok=;
while(T--){
int n,m;cin>>n>>m;
cin>>p;
point P[];
for(int i=;i<p;i++)cin>>P[i].x>>P[i].y;
lose=;
for(int i=;i<p;i++){
lose+=!L[i].set(P[i],P[(i+)%p],n,m);
L[i].num=i;
}
/*for(int i=0;i<p;i++){
cout<<L[i].l1<<' '<<L[i].l2<<' '<<L[i].l3<<'\n';
}*/
sort(L,L+p);
/*for(int i=0;i<p;i++){
cout<<L[i].l1<<' '<<L[i].l2<<' '<<L[i].l3<<'\n';
}*/ /*double sum=0;
for(int i=0;i<p-lose;i++){
sum+=(L[0].l1+L[0].l2+L[0].l3);
L[0].l1=10000000;
int cur=L[0].num;
int pre=(cur+7)%p;
int next=(cur+1)%p;
for(int j=0;j<p;j++){
if(L[j].num==pre)L[j].l3=0;
else if(L[j].num==next)L[j].l1=0;
}
sort(L,L+p);
//for(int i=0;i<p;i++){
// cout<<L[i].l1<<' '<<L[i].l2<<' '<<L[i].l3<<'\n';
//}
}*/////贪心求法
Solve();//深搜求法
if(minRoad==)minRoad=;
if(ok)printf("\n");ok=;
printf("Minimum total length = %.3lf\n",minRoad);
}
}

错误代码

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

 #include <cmath>
#include <cstdio>
#include<algorithm>
using namespace std;
const int maxn = ; double min(double a,double b){return a<b?a:b;}//求2个double中较小的一个 const double eps = 1e-;
double sgn(double x) {return fabs(x)<eps?:(x>?:-);}//经典比较2个double类数的方法,相等0,大于1,小于-1
//------------------------------------------------------------------
//2维几何模板
//------------------------------------------------------------------
struct Point{//点类(x,y)构造函数+==重定义
double x,y;
Point(double tx=,double ty=){x=tx;y=ty;}
bool operator == (const Point& t) const {
return sgn(x-t.x)== && sgn(y-t.y)==;
}
}p[maxn],Set[maxn],st[maxn],tmp[maxn],pp[maxn]; 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两点的距离
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的差积 struct Seg{Point s,e;};//射线se
bool outside(Seg seg,Point p){return cross(seg.s,seg.e,p)>eps;}//点p在射线seg左
bool inside(Seg seg,Point p){return cross(seg.s,seg.e,p)<-eps;}//点p在射线seg右 Point Intersect(Point p1, Point p2, Point p3, Point p4, Point& p) {
double a1, b1, c1, a2, b2, c2, d;
a1 = p1.y - p2.y; b1 = p2.x - p1.x; c1 = p1.x*p2.y - p2.x*p1.y;
a2 = p3.y - p4.y; b2 = p4.x - p3.x; c2 = p3.x*p4.y - p4.x*p3.y;
d = a1*b2 - a2*b1;
if ( fabs(d) < eps ) return false;
p.x = (-c1*b2 + c2*b1) / d;
p.y = (-a1*c2 + a2*c1) / d;
return p;
}//直线p1p2和p3p4的交点存在p中(当且仅当Cross(p1p2,p3p4)非0)
//-----------------------------------------------------------------
double W,H;
int a[],n,pn;
double CUT(Seg seg,Point p[]){
int i,j,tot=;
Point A,B;
A=B=Point(,);
bool s,e;
for(i=;i<pn;i++){//这里A、B交替存储seg与动态凸包p[]的交点,并把新凸包保存在pp中,新的凸包点数保存在tot中
if(!outside(seg,p[i]))pp[tot++]=p[i];//p[i]在射线seg右或上
else {
if(i==&&!outside(seg,p[pn-])){//当前p[i]p[i-1]和seg的交点(当i==0时要特殊处理一下)
B=A;
pp[tot++]=Intersect(seg.s,seg.e,p[i],p[pn-],A);
}
if(i!=&&!outside(seg,p[i-])) {
B=A;
pp[tot++]=Intersect(seg.s,seg.e,p[i],p[i-],A);
}
if(!outside(seg,p[i+])) {//当前p[i]p[i+1]和seg的交点(因为我们已经令p[最后一个的后一个]=p[0]所以不必特殊处理)
B=A;
pp[tot++]=Intersect(seg.s,seg.e,p[i],p[i+],A);
}
}
}
pp[tot]=pp[];//特殊处理尾部
pn=tot;memcpy(st,pp,sizeof(pp));//更新p[]和pn
return dist(A,B);//返回割痕长度
}
int main(){
int i;
while(scanf("%lf%lf",&W,&H)!=EOF){
double ans=1e20;
st[]=st[]=Point(,);//tmp[100]是用来保存原来矩形凸包外四个顶点的,
st[]=Point(,H); //st[100]是切割过程中凸包的顶点
st[]=Point(W,H); //因此对于每种切割方案,刚开始都要将st设为原始矩形凸包,这也是tmp存在的原因
st[]=Point(W,);
memcpy(tmp,st,sizeof(st));
scanf("%d",&n);Seg ts[];
for(i=;i<n;i++) {
scanf("%lf%lf",&p[i].x,&p[i].y);
a[i]=i;//0、1、2.....后面对其全排枚举实现所有切割方法的暴力枚举
}p[n]=p[];
for(i=;i<n;i++) ts[i].s=p[i],ts[i].e=p[i+];
do{
double tlen=;//切割长度
memcpy(st,tmp,sizeof(tmp));
pn=;//pn和st[100]一样是计算过程中的量(对于每种切割方案,其开始要更新为原来的,其过程要变化,pn即凸包st的点数)
for(i=;i<n;i++){//按照获得的全排序列切割
tlen+=CUT(ts[a[i]],st);
}
ans=min(ans,tlen);//求出最小值,保存在ans里
}while(next_permutation(a,a+n));//暴力枚举所有情况next_permutation(a,a+n)是将数组全排列找出
printf("Minimum total length = %.3lf\n",ans);
}
return ;
}
/*
线的交点
1>直线可以用直线上一点P0和方向向量v来表示:直线上所有点P满足P=P0+t*v,其中t为参数;如果已知直线上的2个不同的点A、B,则方向向量
为B-A,所以参数方程为A+(B-A)*t;参数方程可以方便的表示出直线射线和线段,区别仅在于t的范围:直线t无范围,射线t>0,线段t在0~1之间
2>直线交点:设直线分别为P+t*v,Q+t*w,u=PQ,交点在第一条直线上的参数为t1、在第二条直线上的参数为t2,则x、y的坐标可以列出一个方程,
截得:t1=cross(w,u)/cross(v,w),t2=cross(v,u)/cross(v,w),前提要确保分母不为0!!
*/
/*
在STL中,除了next_permutation外,还有一个函数prev_permutation,两者都是用来计算排列组合的函数。
前者是求出下一个排列组合,而后者是求出上一个排列组合。所谓“下一个”和“上一个”,书中举了一个
简单的例子:对序列 {a, b, c},每一个元素都比后面的小,按照字典序列,固定a之后,a比bc都小,c比b大,
它的下一个序列即为{a, c, b},而{a, c, b}的上一个序列即为{a, b, c},同理可以推出所有的六个序列为:
{a, b, c}、{a, c, b}、{b, a, c}、{b, c, a}、{c, a, b}、{c, b, a},其中{a, b, c}没有上一个元素,
{c, b, a}没有下一个元素。
*/

[ACM_几何] Metal Cutting(POJ1514)半平面割与全排暴力切割方案的更多相关文章

  1. poj 1514 Metal Cutting (dfs+多边形切割)

    1514 -- Metal Cutting 一道类似于半平面交的题. 题意相当简单,给出一块矩形以及最后被切出来的的多边形各个顶点的位置.每次切割必须从一端切到另一端,问切出多边形最少要切多长的距离. ...

  2. [ACM_几何] Pipe

    http://acm.hust.edu.cn/vjudge/contest/view.action?cid=28417#problem/B     本题大意: 给定一个管道上边界的拐点,管道宽为1,求 ...

  3. [ACM_几何] Fishnet

      http://acm.hust.edu.cn/vjudge/contest/view.action?cid=28417#problem/C 本题大意:有一个1X1的矩形,每边按照从小到大的顺序给n ...

  4. [ACM_几何] UVA 11300 Spreading the Wealth [分金币 左右给 最终相等 方程组 中位数]

    Problem A Communist regime is trying to redistribute wealth in a village. They have have decided to ...

  5. [ACM_几何] The Deadly Olympic Returns!!! (空间相对运动之最短距离)

    http://acm.hust.edu.cn/vjudge/contest/view.action?cid=28235#problem/B 题目大意: 有两个同时再空间中匀速运动的导弹,告诉一个时间以 ...

  6. [ACM_几何] F. 3D Triangles (三维三角行相交)

    http://acm.hust.edu.cn/vjudge/contest/view.action?cid=28235#problem/A 题目大意:给出三维空间两个三角形三个顶点,判断二者是否有公共 ...

  7. [ACM_几何] Wall

    http://acm.hust.edu.cn/vjudge/contest/view.action?cid=28417#problem/E 题目大意:依次给n个点围成的一个城堡,在周围建围墙,要求围墙 ...

  8. [ACM_暴力][ACM_几何] ZOJ 1426 Counting Rectangles (水平竖直线段组成的矩形个数,暴力)

    Description We are given a figure consisting of only horizontal and vertical line segments. Our goal ...

  9. [ACM_几何] Transmitters (zoj 1041 ,可旋转半圆内的最多点)

    Description In a wireless network with multiple transmitters sending on the same frequencies, it is ...

随机推荐

  1. Java系列笔记(6) - 并发(上)

    目录 1,基本概念 2,volatile 3,atom 4,ThreadLocal 5,CountDownLatch和CyclicBarrier 6,信号量 7,Condition 8,Exchang ...

  2. jsTree搜索排序向上向下

    var _node = null, _all_match = 0, _current_match = 0; $(document).ready(function() { $('#area_settin ...

  3. 手把手教你编写一个具有基本功能的shell(已开源)

    刚接触Linux时,对shell总有种神秘感:在对shell的工作原理有所了解之后,便尝试着动手写一个shell.下面是一个从最简单的情况开始,一步步完成一个模拟的shell(我命名之为wshell) ...

  4. Shape comparison language

      形状比较语言, 九交模型 In this topic About shape comparison language Dimensionality Extensions to the CBM SC ...

  5. RedHat下编译安装Boost

    1.解压boost_1_54_0.tar.gz 2.进入目录后,运行 ./bootstrap.sh ,会生成一个 bjam 的可执行程序 3.运行 ./bjam release install 进行编 ...

  6. java基础九[网络与线程](阅读Head First Java记录)

    网络socket连接 Java API的网络功能包(java.net)已经将底层的TCP连接等都封装好了,我们只需要通过Socket对象来建立客户端和服务器的连接,然后客户端能向服务器发送请求,并接收 ...

  7. 自定义Dialog之信息提示

    对话框对于应用也是必不可少的一个组件,在Android中也不例外,对话框对于一些提示重要信息,或者一些需要用户额外交互的一些内容很有帮助. 自定义Dialog步骤: 1.主要创建Java类,并继承Di ...

  8. LeetCode OJ 331. Verify Preorder Serialization of a Binary Tree

    One way to serialize a binary tree is to use pre-order traversal. When we encounter a non-null node, ...

  9. Select查询语句2

    一.模糊查询 1.语法结构 select*from table_name where column like '%context%' 在使用like运算符时如果不使用通配符“%”,则like的作用与= ...

  10. windbg符号

    Symbol Server (Microsoft): srv*c:\mss*http://msdl.microsoft.com/download/symbols Symbol Server (Citr ...