仓库选址

备注:网易游戏-软件测试岗-2015年校园招聘

一、问题描述:

n个农田线性排列,位置分别为1, 2, ..., n,位置相邻的农田间的距离为1,位置为i的农田每天需要nNeed[i]单位的肥料,现在需要建存储肥料的仓库,把肥料从仓库送到位置为i的农田的代价为nNeed[i]*距离。如果仓库建在位置i,则仓库到位置为i的农田的距离为0,到位置为i-1,i+1的农田的距离为1,以此类推。求建仓库的最佳位置i,使得每天运送肥料到各个农田的总代价最少。

二、问题求解:

1.暴力求解:从1到n依次遍历这n的位置,求仓库建在各个位置所需代价,然后取最少代价,则相应位置即为最优建仓库位置。代码如下:

int pay_i(int n,int *nNeed,int i){
	int pay=0;
	for(int j=1;j<=n;++j){
		pay+=nNeed[j]*(j<i?i-j:j-i);
	}
	return pay;
}

int leastPay(int n,int *nNeed){
	if(n<1||nNeed==NULL){return -1;}
	if(n==1){return 1;}

	int pos=1;
	int min=pay_i(n,nNeed,pos);
	
	for(int sum,i=2;i<=n;++i){
		sum=pay_i(n,nNeed,i);
		if(sum<min){
			min=sum;
			pos=i;
		}
	}
	return pos;
}

此方法容易想到,空间复杂度为O(1),但时间复杂度为O(n^2)。

2.设计技巧1:上述求解中,pay_i()计算中有很多求和过程重复计算是不必要的可以,可以改进为线性复杂度算法。设两个辅助数组a1[],a2[],存储部分和的信息:

a1[1]=0; a1[i]=nNeed[1]+nNeed[2]+...+nNeed[i-1], 2<=i<=n; => a[1]=0; a[i]=a[i-1]+nNeed[i-1], 2<=i<=n;
a2[n]=0; a2[i]=nNeed[i+1]+nNeed[i+2]+...+nNeed[n], 1<=i<n; => a2[n]=0; a2[i]=a2[i+1]+nNeed[i+1], 1<=i<n;

则仓库建在位置i时,送肥料到位置从1到i-1的农田的代价sum1=a1[1]+a1[2]+...+a1[i],送肥料到位置从1+1到n的农田的代价sum2=a2[i]+a2[i+1]+...+a2[n],总代价为sum=sum1+sum2。代码实现如下:

int leastPay_optimize2(int n,int *nNeed){
	if(n<1||nNeed==NULL){return -1;}
	if(n==1){return 1;}
	int *a1=new int[n+1];
	int *a2=new int[n+1];
	a1[1]=0;
	for(int i=2;i<=n;++i){
		a1[i]=a1[i-1]+nNeed[i-1];
	}
	a2[n]=0;
	for(int i=n-1;i>=1;--i){
		a2[i]=a2[i+1]+nNeed[i+1];
	}
	int sum,sum1=0,sum2=0;
	for(int i=n;i>=1;--i){
		sum2+=a2[i];
	}
	int min=sum2,pos=1;
	for(int i=2;i<=n;++i){
		sum1+=a1[i];
		sum2-=a2[i-1];
		sum=sum1+sum2;
		if(sum<min){
			min=sum;
			pos=i;
		}
	}
	delete[] a1;
	delete[] a2;
	return pos;
}

此方法是设计技巧上的改进,容易想到,空间复杂度为O(n),但时间复杂度为O(n)。

3.设计技巧2:方法2已经能够达到很好的时间效率,但空间复杂度仍然较高,可以继续改进。在上述实现中数组a1[],a2[]是为了容易理解而设定的,其实是可以省略掉的。因为在主循环(最后一个循环)中,a1[i],a2[i]只用到一次,并且由a1[i]和a2[i]的定义式可知,它们可以很容易的由a1[i-1]和a2[i+1]递推得出。代码实现如下:

int leastPay_optimize2(int n,int *nNeed){
	if(n<1||nNeed==NULL){return -1;}
	if(n==1){return 1;}

	int sum,sum1=0,sum2=0,sum_1=0,sum_2=0;
	for(int i=n-1;i>=1;--i){
		//calculate the payment when pos is 1
		sum_2+=nNeed[i+1];
		sum2+=sum_2;
	}
	int min=sum2,pos=1;
	for(int i=2;i<=n;++i){
		sum_1+=nNeed[i-1];
		sum1+=sum_1;
		sum2-=sum_2;
		sum_2-=nNeed[i];
		sum=sum1+sum2;
		if(sum<min){
			min=sum;
			pos=i;
		}
	}
	return pos;
}
此方法也是设计技巧上的改进,比较容易理解,空间复杂度为O(1),但时间复杂度为O(n)。

三、问题总结:

总体来说难度一般,方法1很容易想到,方法2也比较容易想出,方法3也比较容易理解。但在时间相对有限的情况下,脑袋容易抽筋。笔试的时候只是给出方法2的解法,并且也不确定一些边界问题是否顾及到。方法3也是在更别人讨论的时候想出的。

纸上手写代码,基本功在平时,多多练习,多多思考,多多讨论,必有收获。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值