奶牛渡河——线性dp

题面

题目描述

Farmer John以及他的N(1 <= N <= 2,500)头奶牛打算过一条河,但他们所有的渡河工具,仅仅是一个木筏。

由于奶牛不会划船,在整个渡河过程中,FJ必须始终在木筏上。在这个基础上,木筏上的奶牛数目每增加1,FJ把木筏划到对岸就得花更多的时间。

  • 当FJ一个人坐在木筏上,他把木筏划到对岸需要M(1 <= M <= 1000)分钟。
  • 当木筏搭载的奶牛数目从i-1增加到i时,FJ得多花分钟才能把木筏划过河
  • 也就是说,船上有1头奶牛时,FJ得花分钟渡河;船上有2头奶牛时,时间就变成分钟。后面 的依此类推。

那么,FJ最少要花多少时间,才能把所有奶牛带到对岸呢?当然,这个时间得包括FJ一个人把木筏从对岸划回来接下一批的奶牛的时间。

输入格式

1行: 2个用空格隔开的整数:N 和 M

2..N+1行: 第i+11个整数:

输出格式

第1行: 输出1个整数,为FJ把所有奶牛都载过河所需的最少时间

样例

样例输入

5 10
3
4
6
100
1

样例输出

50

数据范围与提示

【输入说明】

FJ带了5头奶牛出门。如果是单独把木筏划过河,FJ需要花10分钟,带上1头奶牛的话,是13分钟,2头奶牛是17分钟,3头是23分钟,4头是123分钟,将5头一次性载过去,花费的时间是124分钟。

【输出说明】

Farmer John第一次带3头奶牛过河(23分钟),然后一个人划回来(10分钟),最后带剩下的2头奶牛一起过河(17分钟),总共花费的时间是23+10+17 = 50分钟。

 

分析

很明显,这是一道线性动态规划题目。

定义 f [ i ] 为运送前 i 头奶牛(包括第i头)所需要的花费的总共时间(最优)。

 

首先,人划船过去划船回来,一共是2*M分钟,这一点在任何一次往返都是一样的,每往返一次都要花费这样的时间。

除了最后一次运送,只过去对岸,然后就不会返回来。这个好办,我们在答案的最后减去一个M就可以了。

所以,我们可以先初始化每一个f [ i ] = 2*M

 

那么我们接着分析题干。

就拿题目中的样例来说,

3
4
6
100
1

这是一次运送1,2,3,4,5头奶牛所需要的代价。

每一次运送1头牛:花费3
每一次运送2头牛:花费3+4
每一次运送3头牛:花费3+4+6
每一次运送4头牛:花费3+4+6+100
每一次运送5头牛:花费3+4+6+100+1

可以看到,这个代价,和牛的编号是没有关系的

就拿最优的解来说,我们先不考虑2*M本身自带的代价,光说奶牛的事情:
第一次,带过去编号为1,2,3的三头奶牛,花费的代价是3+4+6
第二次,带过去编号为4,5的两头奶牛,花费的代价是3+4,而不是100+1。

因为每多一头牛,是需要叠加代价的,所以我们使用sum数组来记录Mi的前缀和,如下:
sum[1]=3=3
sum[2]=3+4=7
sum[3]=3+4+6=13
sum[4]=3+4+6+100=113
sum[5]=3+4+6+100+1=114

这样,当我们需要运送p个奶牛的时候,直接调用sum[p]即可。
这个前缀和在读入的时候就可以处理完毕。
1 void Read(){
2     scanf("%d%d",&N,&M);
3     for(int i=1;i<=N;i++){
4         f[i]+=2*M;
5         scanf("%d",&m[i]);
6         sum[i]=sum[i-1]+m[i];
7     }
8 }

读入完毕,那么接下来是动态转移

第1……i……N头奶牛依次遍历

每次到 i 头的时候,我们从1……j……i-1头中选取代价f [ j ] 最小的那个(这个f [ j ] 肯定是之前已经处理好的最优情况),再加上sum [ i - j ] (新运送的这么多奶牛),再加上一个2*M

这个2*M一定要注意。因为 f [ j ]是人家自己已经有的一次运送,后来运送 sum [ i - j ] 的时候是新一次运送,必须加上一个新的2×M

之前我就吃了这个亏,没有加上2×M

如此一来,转移方程也就弄好了:

  f [ i ] = std::min ( f [ i ] , f [ j ] + sum [ i - j ] + 2*M ) ;

最后注意f [ N ]再减去一个M

 

代码


#include<cstdio>
#include<algorithm>
#include<cstring>
const int maxn=2500+1;
int sum[maxn],f[maxn];
int N,M;
void Read(){
    scanf("%d%d",&N,&M);
    for(int i=1;i<=N;i++){
        f[i]+=2*M;int temp=0;
        scanf("%d",&temp);
        sum[i]=sum[i-1]+temp;
    }
}
void Solve(){
    for(int i=1;i<=N;i++){
        f[i]+=sum[i];
        for(int j=1;j<i;j++){
            f[i]=std::min(f[i],f[j]+sum[i-j]+2*M);
        }
    }
}
void Output(){
    printf("%d",f[N]-M);
}
int main(){
    Read();
    Solve();
    Output();
    return 0;
}

祝AC。

 
posted @ 2020-04-15 15:28  Dertangch  阅读(198)  评论(0编辑  收藏  举报