牛客多校第六场 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。