网易2018校招内推编程题 堆棋子

* 问题

小易将n个棋子摆放在一张无限大的棋盘上。第i个棋子放在第x[i]行y[i]列。同一个格子允许放置多个棋子。

每一次操作小易可以把一个棋子拿起并将其移动到原格子的上、下、左、右的任意一个格子中。小易想知

道要让棋盘上出现有一个格子中至少有i(1 ≤ i ≤ n)个棋子所需要的最少操作次数.


输入描述:

输入包括三行,第一行一个整数n(1 ≤ n ≤ 50),表示棋子的个数

第二行为n个棋子的横坐标x[i](1 ≤ x[i] ≤ 10^9)

第三行为n个棋子的纵坐标y[i](1 ≤ y[i] ≤ 10^9)



输出描述:

输出n个整数,第i个表示棋盘上有一个格子至少有i个棋子所需要的操作数,以空格分割。行末无空格


如样例所示:

对于1个棋子: 不需要操作

对于2个棋子: 将前两个棋子放在(1, 1)中

对于3个棋子: 将前三个棋子放在(2, 1)中

对于4个棋子: 将所有棋子都放在(3, 1)中


输入例子1:

4

1 2 4 9

1 1 1 1


输出例子1:

0 1 3 10


网易2018校招内推编程题  堆棋子


 * 思路:

 * 这题一开始想错误,首先用的暴力算法,对每个x坐标 xMin<=x<=xMax  y坐标 yMin<=y<=yMax 的点,全部都求一遍到这n个棋子点的距离。

 * 如上图 红色包围区域为需要计算点的范围

 * 结果程序内存超标

 * 修改了内存占用之后,时间又超标。 

 * 后来在牛客网看到了@蟹粉馅大糖包  的分析:

 * “暴力枚举法居然过了,关键在于,最后堆棋子的那个格子,横纵坐标必然在棋子初始的横纵坐

标中间

用反证法,xy轴其实是独立的,先只考虑x坐标,假设把k个棋子堆到x0格子所用的步骤最少,

a个棋子初始在x0的左边,b个棋子初始在x0的右边,且a>b,那么必然存在横坐标为x0-1的格

子,这k个棋子到x0-1的步数会更少,b>a的情况,那么x0+1的目标将比x0更优,至于a=b,

x0-1 和x0的步数是一样的。因此,最终汇聚棋子的x坐标只要在棋子初始的x个坐标中考虑”

所以,只需要计算如下图红色线的交叉点距离所有棋子的距离。

网易2018校招内推编程题  堆棋子


java代码:


public class PilePieces {
	
	public static int n;
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		n = scanner.nextInt();
		int[] xArr = new int[n];
		int[] yArr = new int[n];
		int[] result=new int[n];//存放最终结果的数组,result[0]初始化为0,其余初始化为Integer.MAX_VALUE;
		for (int i = 0; i < n; i++) {
			if(i!=0)
				result[i]=Integer.MAX_VALUE;
			xArr[i]=scanner.nextInt();
		}
		for (int i = 0; i < n; i++) {
			yArr[i]=scanner.nextInt();
		}
	
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				int[] arrTemp=new int[n];//临时数组,用来记录点(xArr[i],yArr[j])到第j2个棋子的距离。
				for (int j2 = 0; j2 < n; j2++) {
					int xDis=xArr[j2]-xArr[i];//取x轴的差
					int yDis=yArr[j2]-yArr[j];//取y轴的差
					xDis=xDis<0?-xDis:xDis;//取正距离
					yDis=yDis<0?-yDis:yDis;//取正距离
					arrTemp[j2]=xDis+yDis;//距离实际上是x轴方向的距离与y轴方向的距离之和。
				}
				Utils.getInstance().sortSmallToBig(arrTemp, 0, n-1);//对点(xArr[i],yArr[j])的距离数组进行从小到大排序。
//				对排序好的临时数组求前k项和,即是点(xArr[i],yArr[j])到最近k个棋子的距离。
//				也就是把k个棋子移动到该点所需要的最少步数。
				for (int k = 1; k < n; k++) {
					int count=0;//最少步数
					for (int k2 = 0; k2 <= k; k2++) {
						count+=arrTemp[k2];
					}
//					将最少步数和结果数组的相应位置比较,如果比他小,则将最少步数赋给结果数组相应位置。
					if(count<result[k])
						result[k]=count;
				}
			}
		}
			
//		输出结果
		for (int i = 0; i < n-1; i++) {
			System.out.print(result[i]+" ");
		}
		System.out.print(result[n-1]);	
	}	
}

class Utils{
	public static Utils sort=new Utils();
	private Utils(){	
	}
	
	public static Utils getInstance(){
		return sort;
	}
//	smallToBig和sortSmallToBig是快速排序。 针对数组进行从小到大的排序, 
//	调用 sortSmallToBig(int[] array, int start, int end)  传入数组  0  和数组.lenth-1.
	private int smallToBig(int[] array, int start, int end) {
		int key = array[start];
		while (start < end) {
			while (array[end] >= key && end > start) {
				end--;
			}
			array[start] = array[end];
			while (array[start] <= key && end > start) {
				start++;
			}
			array[end] = array[start];
		}
		array[end] = key;
		return end;
	}

	public void sortSmallToBig(int[] array, int start, int end) {
		if (start >= end) {
			return;
		}
		int local = smallToBig(array, start, end);
		sortSmallToBig(array, start, local - 1);
		sortSmallToBig(array, local + 1, end);
	}
}



上一篇:Python编程语言学习:判断变量是否为NONE或False的几种常见写法(if not用法教程)


下一篇:网易2018校招内推编程题 交错01串