一道很有趣的题目,主要是考思维,题目大概意思是有一群人需要过河,每个人过河都需要一定的时间,有一艘船可以容纳两个人,但是两个人上船过河,所花时间为两人中的较大者,问所有人过河的最短时间。
容易想到一种方案:花费时间最少的人和花费时间最多的人一起,然后时间最少的人回来。因为时间少的人会被时间多的人“拖累”,既然时间多的人不得不花这么长的时间过河,并且船还得回来,所以不妨让时间最少的人和他一起,由时间最少的人开船回来,这样额外时间(指一个人开船回来花的时间)会尽量少。
如果每次都这样做,总时间为 ( n - 2 ) * cost[ 0 ] + cost[ 1 ] + cost[ 2 ] + ...... + cost[ n - 1 ] ( cost[] 中元素按所花时间递增排序)。但是我们注意到,这样做会使得那些花费时间多的人都要走一次,虽然有时间花费最少的人将船开回来,但是,“不管你快还是不快,我就是那么慢”,所以如果我们将最慢的和次慢的放在一艘船里,过河的时间(不计返程)同最慢的和最快的在一艘船里的时间是相同的。
因此,我们可以让最慢的和次慢的过去,由最快的开船回来,这样就比每次只能送一个慢的要划算,但是这样每次必须要将最快的留在对面,让他在最慢的和次慢的过河之后再把船开回来,也就是说,最快的那个人,需要在最慢的和次慢的过河前,到对岸等着,在他们过河之后将船开回,由于最快的那个人过河之后,船还得回来,所以我们还需要一个人同最快的那个人一起过河,这次自然是选快的,所以我们选次快的。
所以第二种方案的全过程是:最快和次快的人先过河,然后最快的人回来,然后最慢和次慢的人上船,过河,次快的人回来。
由于第一种方案是尽量快,第二种方案是尽量多送耗时长的人,所以不能简单地根据贪心策略来求,而是用动态规划的思想,获得全局最优解。
状态转移方程为:
dp[ i ] = min{ dp[ i - 1 ] + cost[ 0 ] + cost[ i ] , dp[ i - 2 ] + cost[ 0 ] + cost[ 1 ]*2 + cost[ i ] }
即对第i个人,考虑使用使得总时间最少的一种方案。
下面是代码:
#include#include using namespace std;const int MAX_PPL = 1010;int main(){ int t , cost[ MAX_PPL ] , dp[ MAX_PPL ]; cin>>t; while( t-- ) { int pplNum; cin>>pplNum; for( int i = 0 ; i < pplNum ; i++ ) { cin>>cost[ i ]; } sort( cost , cost + pplNum ); dp[ 0 ] = cost[ 0 ]; dp[ 1 ] = cost[ 1 ]; for( int i = 2; i < pplNum ; i++ ) { dp[ i ] = min( dp[ i - 1 ] + cost[ 0 ] + cost[ i ] , dp[ i - 2 ] + cost[ 0 ] + cost[ 1 ]*2 + cost[ i ]); } cout<