[ACM_几何] Metal Cutting(POJ1514)半平面割与全排暴力切割方案
Description
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
Output
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)半平面割与全排暴力切割方案的更多相关文章
- poj 1514 Metal Cutting (dfs+多边形切割)
1514 -- Metal Cutting 一道类似于半平面交的题. 题意相当简单,给出一块矩形以及最后被切出来的的多边形各个顶点的位置.每次切割必须从一端切到另一端,问切出多边形最少要切多长的距离. ...
- [ACM_几何] Pipe
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=28417#problem/B 本题大意: 给定一个管道上边界的拐点,管道宽为1,求 ...
- [ACM_几何] Fishnet
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=28417#problem/C 本题大意:有一个1X1的矩形,每边按照从小到大的顺序给n ...
- [ACM_几何] UVA 11300 Spreading the Wealth [分金币 左右给 最终相等 方程组 中位数]
Problem A Communist regime is trying to redistribute wealth in a village. They have have decided to ...
- [ACM_几何] The Deadly Olympic Returns!!! (空间相对运动之最短距离)
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=28235#problem/B 题目大意: 有两个同时再空间中匀速运动的导弹,告诉一个时间以 ...
- [ACM_几何] F. 3D Triangles (三维三角行相交)
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=28235#problem/A 题目大意:给出三维空间两个三角形三个顶点,判断二者是否有公共 ...
- [ACM_几何] Wall
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=28417#problem/E 题目大意:依次给n个点围成的一个城堡,在周围建围墙,要求围墙 ...
- [ACM_暴力][ACM_几何] ZOJ 1426 Counting Rectangles (水平竖直线段组成的矩形个数,暴力)
Description We are given a figure consisting of only horizontal and vertical line segments. Our goal ...
- [ACM_几何] Transmitters (zoj 1041 ,可旋转半圆内的最多点)
Description In a wireless network with multiple transmitters sending on the same frequencies, it is ...
随机推荐
- {VS2010C#}{WinForm}{ActiveX}VS2010C#开发基于WinForm的ActiveX控件
在VS2010中使用C#开发基于WinForm的ActiveX控件 常见的一些ActiveX大部分是使用VB.Delphi.C++开发,使用C#开发ActiveX要解决下面三个问题: 使.NET组件可 ...
- nginx环境下配置nagios-关于perl-fcgi.pl
配置文件如下: 请注意,网上提供的官方文档在运行时可能会出现问题,此文中保证无问题. ; ; ; ; ); ; ); ; ; my $pidnumber = $$; ...
- SCI写作经验交流,别人的经验借鉴下,很有用的!
http://www.dxy.cn/bbs/topic/27127771 语言是非英语国家论文的最大障碍.首先是时态和语态:在前言和讨论里,描述该研究的过去历史和现状时,要使用相应的时态:过去就使用过 ...
- [原创]Spring MVC 学习 之 - URL参数传递
原文参考地址: http://www.cnblogs.com/rhythmK/p/3971191.html 目的和缘由: 本人想做一个分享的页面,分析给朋友注册,注册按分享ID进行级联; 过程: 很多 ...
- .NET实现高效过滤敏感查找树算法(分词算法):
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- java多线程学习-ThreadLocal
为了凑字,把oracle文档里介绍ThreadLocal抄过来 public class ThreadLocal<T> extends Object This class provides ...
- 存在网路的情况下重命名SDE中数据图层错误(The orphan junction feature class cannot be renamed)
运行环境为ArcGIS9.3,VS2008. 问题描述:数据通过SDE存储在Oracle10g数据库中,数据集中存在几何网络,在存在网络的情况下通过程序对其中的数据图层进行重命名,弹出"Th ...
- IIS7中的站点、应用程序和虚拟目录详细介绍 (转)
这里说的不是如何解决路径重写或者如何配置的问题,而是阐述一下站点(site),应用程序(application)和虚拟目录 (virtual directory)概念与作用,已及这三个东西在IIS6与 ...
- iOS开发:深入理解GCD 第二篇(dispatch_group、dispatch_barrier、基于线程安全的多读单写)
Dispatch Group在追加到Dispatch Queue中的多个任务处理完毕之后想执行结束处理,这种需求会经常出现.如果只是使用一个Serial Dispatch Queue(串行队列)时,只 ...
- (2015秋) 作业6:(电梯系统之结对编程 I 总分=2*50 分)
电梯系统 0. 背景 南通大学钟秀校区“主教学楼” 用 2 部电梯(连地下室共9层):每到下课.上课前都特别拥挤,大家希望改进已有的电梯调度算法,满足学生和老师快速乘坐电梯的需要.因此,需要重新设计一 ...