这道题有那么一点难度,主要在于设计出求解的方法。这道题有点像磁盘调度的最短路径算法(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;
}