2019牛客暑期多校训练营(第六场)J.Upgrading Technology(暴力+思维)

https://ac.nowcoder.com/acm/contest/886/J

题意:n个技能,m个技能,升级每个技能每一级可能支出,也可能获得收益,当n个技能同时都升到某一等级,还会有福利,可能额外支出,也可能获得额外收益,现在让你求最大收益。

题解:数据小,可暴力,否则dp;   定义支出为负,收益为正

          首先明白官方题解:一种错误的想法是枚举有 j level 升满,然后对第 i 种科技从 pre[i][j] pre[i][m] 中选最小的那个作为第 i 种科技的最终 level(这里 pre[i][j] 表示第 i 种科技升前 j level 的代 价和) 这种做法的问题在于 dj 可能是负的,贪心选取最小的 pre 可能导致 j+1 之后某些负的 level 使答案变差。很不幸,我在这里死活没有跳出来,枚举前J个技能升满后,贪心找到的不是最优情况,会受到 枚举到的J~具体升到级数I之间的额外福利影响。

         所以稍微修改一下,从后往前扫预处理出最优1~J升满后,后面(J~M)升级的最优,保证不收到1~J的额外福利影响。

 for(int i=1;i<=n;i++){//记录枚举的共同等级后面自由升级的最优情况 
            for(int j=m;j>=1;j--){
                if(j==m){
                    mx[i][j]=a[i][j];
                }else{
                    mx[i][j]=max(mx[i][j+1]+a[i][j],a[i][j]);
                }
            }
        }

     接下来就是暴力枚举共同升级的前J个级数的收益+后面的最优收益(I级),会出现这种情况,就是这N个技能后面最优收益级数都大于枚举的J,会多出J~I的福利影响,所以需要把这N个技能后面最优收益最小的内个级数让他就升到J级。

后面的最优收益会有三种情况:1.全正(减去最小正收益即可,只升到J级)2.全负(他们都只升到J级)3.有正有负(只考虑正数收益即可)

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 2e3 + 5;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll MOD = 1e9 + 7;
ll a[maxn][maxn];
ll sum[maxn][maxn];
ll pre[maxn];
ll mx[maxn][maxn];
int main(){
    std::ios::sync_with_stdio(false);
    int t;
    cin>>t; int kase=1;
    while(t--){
        memset(a, 0, sizeof(a));
        memset(sum, 0, sizeof(sum));
        memset(pre, 0, sizeof(pre));
        memset(mx, 0, sizeof(mx));
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=n;i++){//每行前缀和 
            for(int j=1;j<=m;j++){
                cin>>a[i][j];a[i][j]=-a[i][j];
                sum[i][j]=sum[i][j-1]+a[i][j];
            }
        }
        for(int i=1;i<=m;i++){//额外福利前缀和 
            cin>>pre[i];
            pre[i]=pre[i-1]+pre[i];
        }
        for(int i=1;i<=n;i++){//记录枚举的共同等级后面自由升级的最优情况 
            for(int j=m;j>=1;j--){
                if(j==m){
                    mx[i][j]=a[i][j];
                }else{
                    mx[i][j]=max(mx[i][j+1]+a[i][j],a[i][j]);
                }
            }
        }
        ll ans=0;
        for(int i=0;i<m;i++){
            ll mi=mx[1][i+1]; ll tmp=0; ll psum=0;
            for(int j=1;j<=n;j++){
                if(mx[j][i+1]>0) tmp+=mx[j][i+1];
                mi=min(mx[j][i+1],mi);//最小收益 
                psum+=sum[j][i];
            }
            if(mi<0) ans=max(ans,tmp+psum+pre[i]);
            else ans=max(ans,tmp+psum+pre[i]-mi);          
        }
        ll qw=0;
        for(int i=1;i<=n;i++){
            qw+=sum[i][m];
        }
        ans=max(qw+pre[m],ans);
        cout<<"Case #"<<kase++<<": "<<ans<<endl;
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值