牛客多校第六场 J Upgrading Technology dp

题意:

有n个技能,一开始都是0级,第i个技能从j-1级升到j级,花费$c_{i,j}$,但是花费不一定是正的

所有的技能升到j级时,奖励$d_j$但是奖励也不一定是正的

题解:

用sum[i][j]储存-c[i][j]的前缀和,即技能i升到j级后总共的收益

再用w[j]储存f[j]的前缀和,代表所有的技能都升到j的收益。

再开一个数组maxxx[i][j]用于储存技能i至少升到j级的收益,即max(sum[i][j~m])。

然后枚举j,计算$w_j+\sum_{i=1}^{n}maxxx[i][j]$即可

但这样带来一个问题,对于某个j,假如每种技能在最少升到j级的情况下,最优情况都是需要升到更高级,那么,所有的技能都升级带来的奖励d你就必须收下,即便这个d是负的。

因此,定义bonus=maxxx[i][j]-sum[i][j],代表某个技能因为升到高于j得到的额外奖励,当对于某个j,所有的技能带来的bonus都大于0,就减去那个最小的bonus,表示以最小的代价把一个技能打回j级,这样就保证了计算出来的奖励d是正确的。

#include<iostream>
#include<cstring>
#define LL long long
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
LL sum[1005][1005];
LL maxxx[1005][1005];
LL d[1005];

int main(){
//    int i;
//    int j; 
    int t;
    scanf("%d",&t);
//    for(int i=0;i<=1000;i++){
//        for(int j=0;j<=1000;j++){
//            printf("%lld ",sum[i][j]);
//        }
//        printf("\n");
//    }
    for(int tt=1;tt<=t;tt++){
        int n,m;
        scanf("%d %d",&n,&m);
//        memset(sum,0,sizeof sum);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                scanf("%lld",&sum[i][j]);
                sum[i][j]=-sum[i][j];
                sum[i][j]+=sum[i][j-1];
            }
//            for(int j=0;j<=m;j++)printf("%lld ",sum[i][j]);
//            printf("\n");
            maxxx[i][m]=sum[i][m];
            for(int j=m;j>=1;j--){
                maxxx[i][j-1]=max(sum[i][j-1],maxxx[i][j]);
                //最少修炼到第j层时最大收益 
            }
//            for(int j=0;j<=m;j++)printf("%lld ",maxxx[i][j]);
//            printf("\n");
        }
        for(int i=1;i<=m;i++){
            scanf("%lld",&d[i]);
            d[i]+=d[i-1];
        }
        LL maxx=0;
        for(int j=0;j<=m;j++){
            //最少修炼到第j层
            LL now=d[j];
            int bonustimes=0;
            LL minbonus=INF;
            for(int i=1;i<=n;i++){
                now+=maxxx[i][j];
                if(maxxx[i][j]>sum[i][j]){
                    bonustimes++;
                    minbonus=min(minbonus,maxxx[i][j]-sum[i][j]);
                }
            }
//            printf("bun:%lld\n",minbonus);
            if(bonustimes==n)now-=minbonus;
            maxx=max(maxx,now); 
        }
        printf("Case #%d: %lld\n",tt,maxx); 
    }
    return 0;
}

 

PS:

本题带来了一个教训,格式输入一定不要偷懒,要对于所有的数据类型都采用正确的格式符号,即便题目限定了输入范围。

比如在做这道题时,我发现,如果你用%d输入long long,因为%d只给了变量的后32位赋值,但是注意,对于负数,因为补码运算,int 和long long不是简单的截掉头部的关系。

比如你输入-1,32位的-1是11111111111111111111111111111111

但是如果乱用格式符号,导致64位变量只有后32位被赋值,则变成了0000000000000000000000000000000011111111111111111111111111111111

这个数对应的是4294967295

浮点数乱七八糟的就更多了。

正确的格式符号:

scanf:%d->int %lld/%l64d->long long (视编译器而定)  %f->float %lf->double

printf:%f->float/double

如果忽略这点,就会导致莫名其妙的wa。

 

posted @ 2019-08-03 21:46  Isakovsky  阅读(171)  评论(0编辑  收藏  举报