Description
有一个凸p边形(p<=8),我们希望通过切割得到它。一开始的时候,你有一个n*m的矩形,即它的四角的坐标分别为(0,0), (0,m), (n,0), (n,m)。每次你可以选择一条直线把当前图形切割成两部分,保留其中一个部分(另一部分扔掉)切割线的长度为此直线在多边形内部的部分的长度。求出最短的切割线总长度。下面是一个例子。我们需要得到中间的多边形。 分别沿着直线1,2,3,4进行切割即可,得到中间的四边形。
Input
第一行有两个整数n, m(0 < n,m < 500),第二行为一个整数p(3<=p<=8)。以下p行每行为两个整数x, y(0 < x < n, 0 < y < m),为按顺时针给出的各顶点坐标。数据保证多边形的是凸的,无三点共线。输入数据无错误。
Output
仅一行,为最短切割线的总长度,四舍五入到小数点后3位。允许有0.001的误差。
Sample Input
4
80 80
70 30
20 20
20 80
Sample Output
HINT
样例对应于图中给出的例子。
--------------------------------------------------------------------------------------------
发现这道题网上还没有题解。。我就来写第一篇啦
这道题一看就是几何题。。
p个顶点即有p条边,考虑到p的范围这么小,我们可以直接用搜索来尝试所有的切割顺序。。(很容易想到,这里的切割一定是沿着p边形的边切,否则白白增加了总长度,没有意义)
关键的问题就是如何计算出每次切割的长度,在这里我用了解析几何的方法。(用解析几何感觉好像挺非主流的,我觉得应该有计算几何的方法,如果有神牛知道求赐教!!)
先预处理p条边所在直线的方程式(联立方程组计算啦~),其中有一些边与矩形边界重合,不需要切,就先标记上。
计算切某条边所需的长度时,求出此边所在直线与所有已切边所在直线以及矩形边界的交点(又是联立方程组),分别记录这条边的某个点两端的交点距此点距离的最小值,它们的和就是本次切割的线的长度。
具体看代码吧,如果有什么地方不明白可以给我留言哦
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstring>
#include<algorithm>
#include<iostream>
#define Z Line
using namespace std;
const double eps=1e-6;
int xxx=0;
double ans=1e20;
struct Line
{
double a,b,c;
}line[10+5];
int n,m;
struct Point
{
int x,y;
}point[10+5];
bool use[10+5],flag[10+5];
int p;
bool count(double &x,double &y,const Z &m1,const Z &m2)//计算方程组的解
{
double d=m1.a*m2.b-m2.a*m1.b;
if(fabs(d)<eps)return false;
x=(m1.b*m2.c-m2.b*m1.c)/d;
y=(m1.c*m2.a-m2.c*m1.a)/d;
return true;
}
int pre(int i)
{
if(i==1)return p;
else return i-1;
}
double dis(double x1,double y1,double x2,double y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
void calc2(double &best1,double &best2,double x,double y,int i)
{
if(x<point[i].x-eps)
{
best1=min(best1,dis(point[i].x,point[i].y,x,y));
}
else if(x>point[i].x+eps)
{
best2=min(best2,dis(point[i].x,point[i].y,x,y));
}
else
{
if(y>point[i].y+eps)
{
best1=min(best1,dis(point[i].x,point[i].y,x,y));
}
else if(y<point[i].y-eps)
{
best2=min(best2,dis(point[i].x,point[i].y,x,y));
}
else
{
if(point[pre(i)].x>x+eps)best1=min(best1,dis(point[i].x,point[i].y,x,y));
else if(point[pre(i)].x<x-eps)best2=min(best2,dis(point[i].x,point[i].y,x,y));
else
{
if(point[pre(i)].y<y-eps)best1=min(best1,dis(point[i].x,point[i].y,x,y));
else best2=min(best2,dis(point[i].x,point[i].y,x,y));
}
}
}
}
double calc(int i)
{
double best1,best2;//分别表示点point[i]左边的最短距离和右边的最短距离。。。如果分不出左右就是上下
best1=best2=1e20;
double x,y;
if(count(x,y,(Z){1,0,0},line[i]))
{
calc2(best1,best2,x,y,i);
}
if(count(x,y,(Z){1,0,-n},line[i]))
{
calc2(best1,best2,x,y,i);
}
if(count(x,y,(Z){0,1,0},line[i]))
{
calc2(best1,best2,x,y,i);
}
if(count(x,y,(Z){0,1,-m},line[i]))
{
calc2(best1,best2,x,y,i);
}
for(int j=1;j<=p;j++)
{
if(use[j]&&!flag[j])
{
if(count(x,y,line[j],line[i]))
{
calc2(best1,best2,x,y,i);
}
}
}
return best1+best2;
}
void dfs(int now,double sum)
{
if(now==p)
{
ans=min(ans,sum);
return;
}
for(int i=1;i<=p;i++)
{
if(!use[i])
{
double lenth=calc(i);
use[i]=true;
dfs(now+1,sum+lenth);
use[i]=false;
}
}
}
int main()
{
scanf("%d%d",&n,&m);
scanf("%d",&p);
for(int i=1;i<=p;i++)
{
scanf("%d%d",&point[i].x,&point[i].y);
if(i>1)
{
if((point[i].x==point[i-1].x&&point[i].x==n)||(point[i].y==point[i-1].y&&point[i].x==m)){use[i]=true,flag[i]=true,xxx++;}
double k,b;
Z m1={point[i].x,1,-point[i].y};
Z m2={point[i-1].x,1,-point[i-1].y};
if(!count(k,b,m1,m2))
{
if(point[i].x==point[i-1].x)line[i].a=1,line[i].b=0,line[i].c=-point[i-1].x;
if(point[i].y==point[i-1].y)line[i].a=0,line[i].b=1,line[i].c=-point[i-1].y;
}
else
{
line[i].a=k;line[i].b=-1;line[i].c=b;
}
}
}
if((point[1].x==point[p].x&&point[1].x==n)||(point[1].y==point[p].y&&point[1].x==m))flag[1]=true,use[1]=true,xxx++;
double k,b;
Z m1={point[1].x,1,-point[1].y};
Z m2={point[p].x,1,-point[p].y};
if(!count(k,b,m1,m2))
{
if(point[1].x==point[p].x)line[1].a=1,line[1].b=0,line[1].c=-point[p].x;
if(point[1].y==point[p].y)line[1].a=0,line[1].b=1,line[1].c=-point[p].y;
}
else
{
line[1].a=k;line[1].b=-1;line[1].c=b;
}
if(xxx==p)
{
printf("%.3f\n",0);
return 0;
}
for(int i=1;i<=p;i++)
{
if(!use[i])
{
double lenth=calc(i);
// printf("%.3f\n",lenth);
use[i]=true;
dfs(xxx+1,lenth);
use[i]=false;
}
}
printf("%.3f\n",ans);
return 0;
}