P2389 电脑班的裁员
题意:长度为n的序列,选出k个连续的字段,使和最大(有负数)
暴力只选正数且不考虑k的边界问题50(数据。。。)
正解从$O(n^3)到O(n)$不等,($O(n)$不会)
DP
1、$O(n^3)$
以f[i][j]代表前i个数,选j段的最大ans
当前数不选:f[i-1][j]
当前数选:f[l][j-1]+s[i]-s[l](s为前缀和)
#include<cstdio> #include<iostream> #include<cstring> #include<cctype> #include<algorithm> using namespace std; #define int long long #define olinr return #define _ 0 #define love_nmr 0 #define DB double inline int read() { int x=0,f=1; char ch=getchar(); while(!isdigit(ch)) { if(ch=='-') f=-f; ch=getchar(); } while(isdigit(ch)) { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } inline void put(int x) { if(x<0) { x=-x; putchar('-'); } if(x>9) put(x/10); putchar(x%10+'0'); } int a[505]; int n; int s[505]; int k; int tot; int f[505][505]; signed main() { n=read(); k=read(); for(int i=1;i<=n;i++) s[i]=s[i-1]+(a[i]=read()); for(int i=1;i<=n;i++) { for(int j=1;j<=k;j++) { f[i][j]=f[i-1][j]; for(int l=0;l<i;l++) { f[i][j]=max(f[i][j],f[l][j-1]+s[i]-s[l]); } } } put(f[n][k]); olinr ~~(0^_^0)+love_nmr; }
2、$O(n^2)$优化(1)
对于f[l][j-1]-s[l]的最大值可以一边DP一边处理出来
这样就减少了一维的枚举
注意要先枚举j,这样才能处理出
因为l<i,所以在枚举i的时候可以处理出(1---i-1,j)的最大值,直接转移
#include<cstdio> #include<iostream> #include<cstring> #include<cctype> #include<algorithm> using namespace std; #define int long long #define olinr return #define _ 0 #define love_nmr 0 #define DB double inline int read() { int x=0,f=1; char ch=getchar(); while(!isdigit(ch)) { if(ch=='-') f=-f; ch=getchar(); } while(isdigit(ch)) { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } inline void put(int x) { if(x<0) { x=-x; putchar('-'); } if(x>9) put(x/10); putchar(x%10+'0'); } int a[505]; int n; int s[505]; int k; int tot; int f[505][505]; signed main() { n=read(); k=read(); for(int i=1;i<=n;i++) s[i]=s[i-1]+(a[i]=read()); for(int j=1;j<=k;j++) { int maxn=0; for(int i=1;i<=n;i++) { f[i][j]=max(f[i-1][j],maxn+s[i]); maxn=max(maxn,f[i][j-1]-s[i]); } } put(f[n][k]); olinr ~~(0^_^0)+love_nmr; }
3、$O(n^2),空间O(n)$
以f[i][j]代表前i个分成j段,第i个可以不选的ans
以g[i][j]代表前i个分成j段,第j个必须选的ans
g[i][j]=max(g[i-1][j],f[i-1][j-1])+a[i]
f[i][j]=max(f[i-1][j],g[i][j])
显然第一维可以滚动掉(类似01背包倒着枚举)
#include<cstdio> #include<iostream> #include<cstring> #include<cctype> #include<algorithm> using namespace std; #define int long long #define olinr return #define _ 0 #define love_nmr 0 #define DB double inline int read() { int x=0,f=1; char ch=getchar(); while(!isdigit(ch)) { if(ch=='-') f=-f; ch=getchar(); } while(isdigit(ch)) { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } inline void put(int x) { if(x<0) { x=-x; putchar('-'); } if(x>9) put(x/10); putchar(x%10+'0'); } int a[505]; int n; int k; int tot; int g[505]; int f[505]; signed main() { n=read(); k=read(); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<=n;i++) for(int j=k;j>=1;j--) { g[j]=max(g[j],f[j-1])+a[i]; f[j]=max(f[j],g[j]); } put(f[k]); olinr ~~(0^_^0)+love_nmr; }
----olinr