洛谷 P14169 题解
同步发表至洛谷。
这题一看就是动态规划。
我们设 \(f_{i,j,k}\) 表示某老师在第 \(i\) 分钟时站在第 \(j\) 名同学身后,所用的体力为 \(k\) 时,前 \(i\) 分钟所产生的最大总怒气值。
根据题意可以直接写出 DP 式子,具体怎么推出来的不再赘述:
其中 \(q_{i,j}\) 是某老师第 \(i\) 分钟时站在第 \(j\) 名同学身后所增加的怒气值。
初始条件为 \(f_{0,j,0}=0\),其中 \(j\in [1,n]\)。但好像和直接 memset(f,0,sizeof f); 没什么区别。
最终答案是 \(\max_{j=1}^n \max_{k=0}^T f_{m,j,k}\)。
什么你还不会求 \(q\)?
很简单,根据题意,就是让你求区间和,借助前缀和即可。
代码:
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
sum[i][j]=sum[i][j-1]+a[i][j];
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
q[i][j]=sum[i][min(n,j+X)]-sum[i][max(1,j-X)-1];
:::
当然这样的 DP 时间复杂度 \(\mathcal O(mn^2T)\),一定会 \(\textcolor{#052242}{\text{TLE}}\) 的。
我们可以利用七年级知识把原式转换成下面这样:
可以发现,原式的计算主要涉及
两部分。
我们设:
我们发现,\(s_{i,j,k}\) 所统计的答案包含所有 \(s_{i,j+1,k-1}\) 所统计的答案。于是我们希望用 \(s_{i,j+1,k-1}\) 推出 \(s_{i,j,k}\),就有了下面式子:
初始条件为 \(\begin{cases} s_{i,n,k}=f_{i,n,k} \\ s_{i,j,0}=f_{i,j,0} \end{cases}\)。
同理我们设:
则也有:
初始条件为:
上述所有的 \(i\in [1,m],j\in [1,n],k\in [0,T]\)。
那 \(f\) 的 DP 式为:
就这样,我们用 \(\mathcal O(mnT)\) 的时间复杂度出色地完成了本题。
好像没有什么问题了吧……于是我们写完了代码,自信一交:
程序在 \(\text{64MB}\) 里撞出闷响,数组的影子漫过了内存的墙。记录里的 \(\textcolor{#052242}{\text{MLE}}\) 像道锁,困住了我高深算法的光。
哦,忘了,空间复杂度也是 \(\mathcal O(mnT)\),而这题的内存限制还是 \(\text{64MB}\)。
不过我们分析了 \(f,s,t\) 的第一维下标 \(i\),发现它看似没有用处,实则是个累赘,拖着内存的后腿,于是我们果断删掉它。
DP 式就变成了:
当我们把新的代码交上去的那一瞬间,绿色的 \(\textcolor{#52C41A}{\text{AC}}\) 跃上屏幕,好像整个世界都在欢呼。
我们以 \(\mathcal O(mnT)\) 的时间复杂度,\(\mathcal O(nT)\) 的空间复杂度通过了本题。
代码:
#include<iostream>
#include<cstring>
using namespace std;
const int N=315;
int f[N][N],a[N][N],q[N][N],sum[N][N],s[N][N],t[N][N];
int n,m,X,T;
int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9') f=(c=='-'?-1:f),c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*f;
}
void solve(){
n=read(),m=read(),X=read(),T=read();
memset(f,0,sizeof f);
memset(sum,0,sizeof sum);
memset(q,0,sizeof q);
memset(s,0,sizeof s);
memset(t,0,sizeof t);
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
a[i][j]=read();
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
sum[i][j]=sum[i][j-1]+a[i][j];
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
q[i][j]=sum[i][min(n,j+X)]-sum[i][max(1,j-X)-1];
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++)
for(int k=0;k<=T;k++)
f[j][k]=max(s[j][k],t[j][k])+q[i][j];
for(int j=n;j>=1;j--)//这里一定要倒序枚举!!!
for(int k=0;k<=T;k++){
if(j==n) s[j][k]=f[j][k];
else if(k==0) s[j][k]=f[j][k];
else s[j][k]=max(s[j+1][k-1],f[j][k]);
}
for(int j=1;j<=n;j++)
for(int k=0;k<=T;k++){
if(j==1) t[j][k]=f[j][k];
else if(k==0) t[j][k]=f[j][k];
else t[j][k]=max(t[j-1][k-1],f[j][k]);
}
}
int answ=0;
for(int j=1;j<=n;j++)
for(int k=0;k<=T;k++)
answ=max(answ,f[j][k]);
printf("%d\n",answ);
}
signed main(){
int Csdsl;Csdsl=read();//CeShiDianShuLiang
while(Csdsl--) solve();
return 0;
}

浙公网安备 33010602011771号