刚开始刷dp,先来几个水题
题意:把一个长度为n的数组分成不重合的m段,求最大和
思路:显然的dp题
一开始的思路就是,一个二维数组dp[i][j],表示选第j个数并把a[1,j]分成i段
那么就要想这个dp[i][j]是如何来的,可以由dp[i][j-1], 即前j-1个分成i段并且选了a[j-1]这个数,所以可以直接加上去,还有一种情况是可以a[j]自成一段,那么dp[i][j]就等于max(dp[i-1][k])+a[j], 1<=k<=j-1,前j-1个分成i-1段加上a[j]自成一段,那么状态转移方程就出来了
状态转移方程为
dp[i][j]=max(dp[i][j-1], dp[i-1][k])+a[j];
核心代码如下:
while(~scanf("%d%d", &m, &n)){
for(int i = 1; i <= n; i++) a[i]=read();
for(int i = 1; i <= m; i++){
for(int j = i; j <= n; j++){
ll mx = -inf;
for(int k = 1; k < j; k++){
mx = max(mx, dp[i-1][k]);
}
dp[i][j] = max(dp[i][j-1], mx)+a[j];
}
}
ll res = -inf;
for(int i = m; i <= n; i++) res = max(res, dp[m][i]);
printf("%lld\n", res);
}
但是可以注意到n的范围到1e6, m范围不清楚,这种O(n^3)的做法肯定会tle,而且内存也会爆掉,所以进行优化, 我们可以另外再开一个数组来存储max(dp[i-1][k]),进行时间上的优化,同时可以发现dp可以变为滚动数组,变成一维,这样就变成了O(n^ 2) (姑且算成是n^2), 而且不会爆掉内存。
那么具体如何实现呢,首先是对空间的优化,可以看出dp其实要用到的只是dp[i][ * ]和dp[i-1][ * ]对这两个状态进行记录即可
对时间的优化即是dp[i-1][ * ]求最大值 不需要考虑具体的位置k,只要在j-1处记录最大值即可。
#pragma GCC optimize("Ofast","inline","-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
#define rep(i, a, n) for(int i = a; i <= n; i++)
#define per(i, a, n) for(int i = n; i >= a; i--)
#define IOS std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define fopen freopen("file.in","r",stdin);freopen("file.out","w",stdout);
#define fclose fclose(stdin);fclose(stdout);
const int inf = 1e9;
const ll onf = 1e18;
const int maxn = 1e6+10;
inline int read(){
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
return x*f;
}
ll mx[maxn], dp[maxn], a[maxn];
signed main(){
int n, m;
while(~scanf("%d%d", &m, &n)){
rep(i,1,n) a[i]=read();
ll tmp = 0;
rep(i,1,n) mx[i]=dp[i]=0; //初始化
rep(i,1,m){
tmp = -inf;
rep(j,i,n){
dp[j] = max(dp[j-1], mx[j-1])+a[j];
// for(int k = 1; k < j; k++){
// mx = max(mx, dp[i-1][k]);
// }
mx[j-1] = tmp; //这里用mx[j-1]来进行记录,tmp为dp[i][j-1]的最大值
// dp[i][j] = max(dp[i][j-1], mx)+a[j];
tmp = max(dp[j], tmp); // 对tmp进行更新
}
}
printf("%lld\n",tmp );
}
return 0;
}
DP算法优化实践
本文探讨了一个经典的DP(动态规划)问题,即将一个数组分为多个段以求得最大和。通过逐步优化,从最初的O(n^3)复杂度降低至O(n^2),并实现了空间优化。文章详细解释了优化过程,包括使用滚动数组和记录前一状态最大值的方法。
255

被折叠的 条评论
为什么被折叠?



