Sicily. 1419. On the run(牛奶快递)

这道题有那么一点难度,主要在于设计出求解的方法。这道题有点像磁盘调度的最短路径算法(SJF)。其实问题有点变形,变成:怎样选择最短路径的算法似的路径最短。

解题之前说点题外话:我感觉这个地方有点迷糊,也就是说,一个数轴上给定若干个点,从其中某个点出发遍历所有的点所耗的时间,会因其选择遍历先后的次序有关。这个我想了一下觉得似乎不会因次序有关,如果真的是因次序有关,那么在操作系统中的SJF算法的基础就是这个了。

开始解题思路,这道题的思路中,首先有这么一个前提:Crystal必然是从右边出发向左边送奶,从左边出发向右边送奶。不可能出现Crystal会绕过一个客户不送到返回来的时候再送。

那么我们就可以把这个问题分成两个部分了: (用数轴形式思考)Crystal可以选择在距他出发点左侧向客户送奶,也可以选择距他出发点右侧向客户送奶。

这么说似乎还不是很明白,我们把这道题转成编程模型来思考下(一开始我也想不出这个模型,感谢其他的博主!):

设置两个二维数组:t1[i][j],t2[i][j].两个下标i,j均表示楼层,t1[i][j]表示Crystal向左边走到楼层i,向右边走到j,之后停在左边的最短时间;t2[i][j]同样表示其向左边走到楼层i,向右边走到j,之后停在右边的最短时间。

在这个模型下,Crystal从中间出发,向两边选择性前进,最终表示我们想要的那个答案应该是t1[0][N-1]和t2[0][N-1]中的最小值。因此t1[i][j]的前一站一定是t1[i+1][j](从出发点左边来)或者t2[i+1][j](从出发点右边来);同样道理,t2[i][j]的前一站一定是t2[i][j-1](从出发点右边来)或者t1[i][j-1](从出发点左边来)。这两种前进方法再加上其他楼层等待的时间,就是状态转移方程了:

 

t1[i][j] =min(t1[i+1][j]+(s[i+1] - s[i])*(n - (j-i)),t2[i+1][j]+(s[j] - s[i])*(n - (j -i)));

t2[i][j] =min(t1[i][j-1]+(s[j] - s[i])*(n - (j-i)),t2[i][j-1]+(s[j] - s[j-1])*(n - (j -i)));

 

这里计算剩余楼层的客户还需等待时间是用Crystal走过两个客户的时间乘上剩余顾客的数量(注意正在给其送过去的那个顾客也要加上),取最小那种方法,就完成dp了。

 

这道题还有两个比较神奇的地方,首先是在对客户进行数组编入的时候也要把Crystal所在的楼层也编入,也就是说,整个floor数组应该有N+1个元素(这个N是一开始输入的那个N),这样才可以产生dp数组中值为0的元素,保证代码的正确与和谐。之前没注意到这一点,一直和答案对不上。

其次在数组初始化的时候,理论上初始化一个比较大的值就对了,但一开始我初始化为10000的时候居然就WA了,之后debug了很久,就在memset这个函数里一直不知道为什么要等待输入,无法进行下去。后来参照其他博文把初始化的值设为0x6f,就可以AC了,我又试一下其他的值,1000是可以的,110也可以。这里我也没搞懂。


十分感谢博主:http://blog.csdn.net/hxingd/article/details/7346012 与 http://blog.csdn.net/rptotal/article/details/5998612 为我照亮这道题!


/*
 * Sicily1419Ontherun.cpp
 *
 *  Created on: 2014年7月6日
 *      Author: Prophet
 */
#include<stdio.h>
#include<algorithm>
#include<string.h>
#define INF 0x6f
using namespace std;
const int maxn = 1001;
int t1[maxn][maxn],t2[maxn][maxn];
int main(){
	int TestCase;
	scanf("%d",&TestCase);
	while(TestCase--){
		int N,L;
		scanf("%d%d",&N,&L);
		int floor[maxn];
		for(int i=0;i<N;i++)
			scanf("%d",&floor[i]);
		floor[N++]=L;//这里需要把Crystal所在的楼层也加进去
		sort(floor,floor+N);
		int StartPos = 0;
		while(floor[StartPos]<L)//找出Crystal出发的楼层在floor数组中的位置
			StartPos++;
		memset(t1,INF,sizeof(t1));
		memset(t2,INF,sizeof(t2));
		t1[StartPos][StartPos]=t2[StartPos][StartPos]=0;
		for(int i=StartPos;i>=0;i--){
			for(int j=StartPos;j<N;j++){
				if(i<StartPos)
					t1[i][j]=min(t1[i+1][j]+(floor[i+1]-floor[i])*(N-(j-i)),
							t2[i+1][j]+(floor[j]-floor[i])*(N-(j-i)));
				if(j>StartPos)
					t2[i][j]=min(t1[i][j-1]+(floor[j]-floor[i])*(N-(j-i)),
							t2[i][j-1]+(floor[j]-floor[j-1])*(N-(j-i)));
			}
		}
		printf("%d\n",min(t1[0][N-1],t2[0][N-1]));
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值