算法:螺旋折线问题

引言:2018蓝桥杯 Java B 个人解析
该题方法很多,就不列举了。

题目7:螺旋折线

如图p1所示的螺旋折线经过平面上所有整点恰好一次。
对于整点(X, Y),我们定义它到原点的距离dis(X, Y)是从原点到(X, Y)的螺旋折线段的长度。

例如dis(0, 1)=3, dis(-2, -1)=9

给出整点坐标(X, Y),你能计算出dis(X, Y)吗?

【输入格式】
X和Y

对于40%的数据,-1000 <= X, Y <= 1000
对于70%的数据,-100000 <= X, Y <= 100000
对于100%的数据, -1000000000 <= X, Y <= 1000000000

【输出格式】
输出dis(X, Y)

【输入样例】
0 1

【输出样例】
3

资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
不要使用package语句。不要使用jdk1.7及以上版本的特性。
主类的名字必须是:Main,否则按无效代码处理。
这里写图片描述


我在做的时候首先想到的就是把原图分为4个象限,不同位置的点使用不同的函数计算。
有个规律,每一圈,左下角的点的值都是所在边的长度的平方。
例如:dis(-1,0)=1,dis(-2,-1)=9.
下面我给出部分dis值的参照表,方便参考。

坐标dis值
0,00
-1,01
-1,12
0,13
1,14
1,05
1,-16
0,-17
-1,-18
-2,-19
…………
-6,-1125
…………
2,8250
…………

这里写图片描述
方法一、
如图2所示,将折线按颜色分为4个区域,对应的计算函数为left,up,right,down。
左下角点(-3,-2)的值恰为其所在边长度的平方,也就是(2*(-x)-1)^2。
那么,该边上其他点的dis值就是该点到左下角点的距离加上左下角点的值,dis(x,x+1)。
左上角点有其位置的特殊性,坐标均为(x,-x),因此其值恰为dis(左下角点)+|x|。
有个距离函数:Point.distance(x1,y1,x2,y2);返回两点的距离的double值。
其他边依次类推,上一边的最后一个点,也是下一边的第一个点。

import java.awt.Point;
import java.util.Scanner;


public class Main {

	public static int left(int x,int y){
		
		if((x+1)==y)return(int)(2*(-x)-1)*(2*(-x)-1);
		//左下角坐标,dis(x,y)=(2*(-x)-1)^2
		if(y==-x)
		return (int)((2*(-x)-1)*(2*(-x)-1)+Point.distance(x, y, x, x+1));
		//左上角坐标,dis(x,y)=dis(左下角)+point.distance(左下角,左上角);
		//左下角-左上角之间的其他任意点的值等于左下角的值加上该点与左下角的距离
		if(y!=x||y!=-x)			
		return(int) (left(x,x+1)+Point.distance(x, y, x, x+1));
		return 0;
	
	}
	public static int up(int x,int y){
		if(x==y)return(int) (left(-x,y)+Point.distance(x,y,-x,y));
		if(x!=-y||x!=y)
		return(int) (left(-y,y)+Point.distance(x, y, -y, y));
		return 0;
	}
	public static int right(int x,int y){
		if(y!=x||y!=-x)return (int)(up(x, x)+Point.distance(x, y, x, x));
		return 0;
		
	}
	public static int down(int x,int y){
		if(x!=-y||x!=y)
		return (int) (right(-y,y)+Point.distance(x, y, -y, y));
		return 0;
		
	}
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		int x=sc.nextInt();
		int y=sc.nextInt();
		int sum=0;
			int start = (int) System.nanoTime();
		if(x<0 && (y>=x+1&&y<=-x)) {//left
			sum = left(x,y);
		}
		if(x>0 && (y>=-x&&y<=x)) {//right
			sum = right(x, y);
		}
		if(y>0 && (x>=-y&&x<=y)) {//up
			sum = up(x, y);
		}
		if(y<0 && (x>=y-1&&x<=-y)) {//down
			sum = down(x, y);
		}
		int end = (int) System.nanoTime();
		System.out.println("耗时:"+(end-start)+"ns(或 "+(end-start)/1000000+" ms)");
		System.out.println(sum);
		System.out.println(sum);
	}

}

方法二、
这里写图片描述
大致思想为:将折线分为3部分,①:黑线-黄线之间 ②:黄线-紫线之间 ③: 紫线-黑线之间
同方法一所述,区域①部分点的dis值均可基于左下角点计算,即满足(x,x+1)(x<0)的点。
区域②可基于右上角点减去两点之间距离计算。左上角点(x==y)的dis值满足(x+y)^2。
区域③即可通过左下角点和右上角点结合计算。
方法三、
这里写图片描述


感谢评论区林鸿清读者的勘误,算法思想及其实现是正确的,对于没有返回预期输出的问题,原因是main函数中的判断有误,现修改中。


2019年8月14日 22:56:46

上面的错误引申出一个新问题,如何判断一个点在left,up,right,down某个区域?
即,这个点是x<0,平行于y轴,还是x>0,平行于y轴,或是y>0,平行于x轴,或是y<0,平行于x轴?
解决了这个问题,整个问题就解决了。

2019年8月14日 23:12:32

已经修改了原来错误的main方法。不能简单使用x<0,x>0,y<0,y>0进行判断,应该将点精确到
left,up,right,down所在范围的线段上。

2019年8月14日 23:17:39

新增了(-6,-1)=125,(2,8)=250两组测试数据

附:另一位博主解法,可供本文算法结果校验。
https://blog.csdn.net/Heloiseeeeeee/article/details/87987709

2019年8月14日 23:26:05

添加了程序运行时间统计,单位为纳秒。ps:1ms=1000000ns,时间太短,ms统计已经为0.
测试了几组大数。结果如下:
在这里插入图片描述
PS:10亿时,性能还是很好。
在这里插入图片描述
在这里插入图片描述

由于本人水平有限,如有错误之处,欢迎指出。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jerryzhou;

您的鼓励,将是我的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值