2021年五月六日中午训练
作者:互联网
最近在做中午练习时发现一道很妙的题,叫做"火车头"。
题目描述
宏帆八中有一个火车站,里面有3个火车头和n个火车厢,每个火车厢里面对应的有一定的人数。规定每个火车头最多拉m个连续的火车厢而且他们拉的车厢一定是从左到右连续的,问它能够拉的最多的人数;
输入格式
第一行:一个整数T(1 <= t <= 11),代表测试样例个数。
接下来T组测试数据:
第一行:一个正整数n,代表火车厢个数,(n <= 50000)
第二行:n个正整数,分别表示第i个火车厢能装的人的个数(均不超过100)
第三行:一个正整数m,代表能够被单一火车头拉动的最大火车厢个数(<= n / 3)
输出格式
一个正整数,代表能够被3个火车头拉动的最大的人数。
样例
样例输入
1
7
35 40 50 10 30 45 60
2
样例输出
240
思路
错误思路
贪心
拿到这个题,我第一时间想到了贪心,用一个ans数组来存储m个和的最大值,然后依次按最大的选,但我们发现:排序就会影响原本的顺序,也有可能会选重复。又因为这道题是一道动态规划的题,所以只能用动态规划做。
正确思路
动态规划
当然,我们也必须要用一个ans数组来存储m个和的最大值。
顺带一提,我最开始求ans数组是用的O(
n
2
n^2
n2)(两层循环)做的,但可以用一个专门求区间和的东西来优化,就是前缀和
前缀和公式
sum[i] = sum[i - 1] + a[i]
接下来我们定义状态
dp[i][j]:表示前i个车厢进入j个区间能装载的总人数
转移我们就抓住i项选不选
若选
那么i - x + 1项也会选
即dp[i][j] = dp[i - x][j - 1] + ans[i - x + 1]
若不选
那么dp[i][j] = dp[i - 1][j]
代码
#include<bits/stdc++.h>
using namespace std;
long long T,n,a[50005],x,dp[50005][4],ans[50005],sum[50005];
int main(){
cin >> T;
while(T--){
memset(ans,0,sizeof(ans));//初始化
memset(dp,0,sizeof(dp));//初始化
memset(sum,0,sizeof(sum)); //初始化
cin >> n;
for(int i = 1;i <= n;i++){
cin >> a[i];
sum[i] = sum[i - 1] + a[i]; //前缀和
}
cin >> x;
for(int i = 1;i <= n - x + 1;i++){
ans[i] = sum[i + x - 1] - sum[i - 1];//求出连续x个的值
}
for(int i = x;i <= n;i++){
for(int j = 1;j <= 3;j++){
dp[i][j] = max(dp[i - 1][j],dp[i - x][j - 1] + ans[i - x + 1]); //状态转移
}
}
cout << dp[n][3] << endl;
}
return 0;
}
今天就和大家分享到这里,大家再见!!!
标签:六日,火车头,int,sum,车厢,2021,五月,ans,dp 来源: https://blog.csdn.net/xuyuetan/article/details/116464115