题目描述
池塘中有n片荷叶恰好围成了一个凸多边形,
有一只小青蛙恰好站在1号荷叶上,
小青蛙想通过最短的路程遍历所有的荷叶(经过一个荷叶一次且仅一次),
小青蛙可以从一片荷叶上跳到另外任意一片荷叶上。
输入数据(frog.in)
第一行为整数n,荷叶的数量。
接下来n行,每行两个实数,为n个多边形的顶点坐标,
按照顺时针方向给出。保证不会爆double。
输出数据(frog.out):
遍历所有荷叶最短路程,请保留3位小数,无Checker。
样例输入和输出
4
50.0 1.0
5.0 1.0
0.0 0.0
45.0 0.0
50.211
数据范围:
对于所有数据,0
题解
青蛙的烦恼
可以看出来,青蛙遍历的最短路线一定不会相交。
很明显,右图比左图优,可以由三角形的三边关系定理证明。
这样,我们可以得出一个结论,就是青蛙在一号结点只能跳到2号结点或者n号结点。
如果青蛙跳到了2号结点,则问题转化为:从2出发,遍历2..n一次仅一次的最短距离。
如果青蛙跳到了n号结点,则问题转化为:从n出发,遍历2..n一次仅一次的最短距离。
这实际上是递归的思维,把问题转化为了本质相同但规模更小的子问题。
根据这个思维,定义状态:
f[s,L,0]表示从s出发,遍历s..s+L-1一次且仅一次的最短距离,
f[s,l,1]表示从s+L-1出发,遍历s..s+L-1一次且仅一次的最短距离。
则状态转移方程为:
F[s,L,0]=min{ (跳到s+1) f[s+1,L-1,0]+dis(s,s+1),(跳到s+L-1) f[s+1,L-1,1]+dis(s,s+L-1)}
F[s,L,1]=min{(跳到s) f[s,L-1,0] + dis(s,s+L-1),(跳到s+L-2) f[s,L-1,1] + dis(s+L-1,s+L-2)}
状态总数为n^2,状态转移的复杂度为O(1),总的时间复杂度为O(n^2)
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
struct node{
double x,y;
}h[1505];//每片荷叶的坐标
double minn(double a,double b)
{
if(a>b)return b;
return a;
}
double sqr(double x)
{
return x*x;
} //乘方
double f[750][750][2],dis[1505][1505];
int main(){
int n;
freopen("frog.in","r",stdin);
freopen("frog.out","w",stdout);
scanf("%d",&n);
memset(f,0x7f,sizeof f); //记得初始化
for(int i=0;i<n;++i)
{
scanf("%lf%lf",&h[i].x,&h[i].y);
h[i+n].x=h[i].x,h[i+n].y=h[i].y;
f[i][1][0]=0,f[i][1][1]=0;
}
for(int i=0;i<n*2;++i)
for(int j=0;j<n*2;++j)
dis[i][j]=sqrt(sqr(h[i].x-h[j].x)+sqr(h[i].y-h[j].y));
//算出任意两片荷叶的距离
for(int l=2;l<=n;++l)
for(int s=0;s<n;++s)
{
f[s][l][0]=minn(dis[s][s+1]+f[s+1][l-1][0],dis[s][s+l-1]+f[s+1][l-1][1]);
f[s][l][1]=minn(dis[s+l-1][s+l-2]+f[s][l-1][1],dis[s+l-1][s]+f[s][l-1][0]);
}
//状态转移方程的解释在上面解释得很详细了
printf("%.3lf",f[0][n][0]); //因为是从1开始遍历所有的点
return 0;
}