首先找到一个都能够达到的最小的j,将所有技能都达到一个大于等于j级的最大收益的等级。再减去一个收益最小的,以保证指示j等级的是该最小收益技能的j等级。
st表+RMQ(824ms)
#include<bits/stdc++.h>
#define MAXN 1200
#define ll long long
#define inf 999999999999999
using namespace std;
ll dp[MAXN][MAXN][15];
ll mp[MAXN][MAXN];
ll rew[MAXN];
int n,m;
void RMQ_INIT(){
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
dp[i][j][0]=mp[i][j];
}
}
for(int i=1;i<=n;i++){
for(int k=1;(1<<k)<=m+1;k++){//多加了一位0
for(int j=0;j+(1<<k)-1<=m;j++){
dp[i][j][k]=max(dp[i][j][k-1],dp[i][j+(1<<k-1)][k-1]);
}
}
}
}
ll RMQ(int l,int r,int i){
int k=log2(r-l+1);
return max(dp[i][l][k],dp[i][r-(1<<k)+1][k]);
}
void init(){
memset(dp,0,sizeof(dp));
memset(mp,0,sizeof(mp));
memset(rew,0,sizeof(rew));
}
int main(){
int T;
cin>>T;
for(int pa=1;pa<=T;pa++){
cin>>n>>m;
init();
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int t;
scanf("%d",&t);
mp[i][j]=mp[i][j-1]-t;//存相反数表示价值
}
}
RMQ_INIT();
for(int i=1;i<=m;i++){
int t;
scanf("%d",&t);
rew[i]=rew[i-1]+t;
}
ll ans=0;
for(int j=0;j<=m;j++){
ll sum=0;
ll mi=inf,u=0;
for(int i=1;i<=n;i++){
ll t=RMQ(j,m,i);
sum+=t;
if(mi>t-mp[i][j]){
u=i;
mi=t-mp[i][j];
}
}
sum+=rew[j];
sum-=mi;
ans=max(ans,sum);
}
printf("Case #%d: %lld\n",pa,ans);
}
return 0;
}
前缀和+后缀最大值(698ms)
#include<bits/stdc++.h>
#define maxn 1005
#define ll long long
using namespace std;
int T,n,m;
ll dp[maxn][maxn],mx[maxn][maxn];
int main()
{
scanf("%d",&T);
for(int pa=1;pa<=T;pa++){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int t;
scanf("%d",&t);
dp[i][j]=dp[i][j-1]-t;
}
mx[i][m+1]=-1ll<<60;
for(int j=m;j>=0;j--){
mx[i][j]=max(mx[i][j+1],dp[i][j]);//维护后缀最大值(最大收益)
}
}
ll ans,plus,sum;
ans=plus=0;
for(int j=0;j<=m;j++){
if(j) {
int t;
scanf("%plus",&t);
plus+=t;
}
sum=-1ll<<60;
for(int i=1;i<=n;i++) if(dp[i][j]-mx[i][j]>sum) {//收益最小的 也就是能够全部达到的最大的j
sum=dp[i][j]-mx[i][j];//计算和时需要减去 保证当前最小的j列不变
}
for(int i=1;i<=n;i++) sum+=mx[i][j];
ans=max(ans,sum+plus);
}
printf("Case #%d: %lld\n",pa,ans);
}
}