奶牛渡河——线性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+1
为1
个整数:
输出格式
第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。