POJ 1700

  一道很有趣的题目,主要是考思维,题目大概意思是有一群人需要过河,每个人过河都需要一定的时间,有一艘船可以容纳两个人,但是两个人上船过河,所花时间为两人中的较大者,问所有人过河的最短时间。

  容易想到一种方案:花费时间最少的人和花费时间最多的人一起,然后时间最少的人回来。因为时间少的人会被时间多的人“拖累”,既然时间多的人不得不花这么长的时间过河,并且船还得回来,所以不妨让时间最少的人和他一起,由时间最少的人开船回来,这样额外时间(指一个人开船回来花的时间)会尽量少。

  如果每次都这样做,总时间为 ( 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<iostream>
#include<algorithm>

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<<dp[ pplNum - 1 ]<<endl;
    }
    return 0;
}

 

posted @ 2013-04-03 21:41  等待电子的砹  阅读(195)  评论(0编辑  收藏  举报