luogu P5662 纪念品

题面传送门
说实话, c c f ccf ccf又抄袭原题惹。而且居然是这道题的弱化版。不应该像是这样的原题的稍微弱化版吗?
言归正传,这道题看似像模拟。又可以爆搜,不过复杂度太高。也太难打,所以略微思考一下,打了一个五重循环的 d p dp dp。看看太恶心,再加上样例也没过,于是就放下了。打了一个贪心保底:

#include<bits/stdc++.h>
using namespace std;
inline int max(register int a,register int b) {
    return a>b?a:b;
}
int n,m,t,a[139][139],l[139][139],r[139][139],ans,fs[139][139],flag1,flag2,tot,fx[139][139];
int main() {
//  freopen("souvenir.in","r",stdin);
//  freopen("souvenir.out","w",stdout);
    register int i,j,k;
    scanf("%d%d%d",&t,&n,&m);
        for(i=1; i<=t; i++) {
            for(j=1; j<=n; j++) scanf("%d",&a[i][j]);
        }
        for(i=1; i<=t; i++) {
            for(j=1; j<=n; j++) {
                flag1=flag2=0;
                for(k=i+1; k<=t; k++) {
                    if(a[k][j]<a[i][j]&&!flag1) flag1=1,l[i][j]=k;
                    if(a[k][j]>a[i][j]&&!flag2) flag2=1,r[i][j]=k;
                }
            }
        }
        for(i=0;i<=t;i++){
            for(j=0;j<=n;j++) fs[i][j]=m;
        }
        for(i=1; i<t; i++) {
            for(j=1; j<=n; j++) {
                if(l[i][j]&&(!r[i][j]||r[i][j]>=l[i][j])) fs[i][j]=fs[i-1][j]+fx[i-1][j]*a[i][j],fx[i][j]=0;
                if(r[i][j]&&(!l[i][j]||r[i][j]<=l[i][j])) fx[i][j]=fx[i-1][j]+fs[i-1][j]/a[i][j],fs[i][j]=fs[i-1][j]-fs[i-1][j]/a[i][j]*a[i][j];
            }
        }
        for(i=1; i<=n; i++) ans=max(ans,fs[t-1][i]+fx[t-1][i]*a[t][i]);
        printf("%d",ans);
    return 0;
}

考完后在 l u o g u luogu luogu上交了一遍, 25 25 25分。
觉得贪心没有组合,不保险,于是想了一个小时五十分钟的 d p dp dp,结果很悲惨的发现只剩十分钟了。我的 T 3 T3 T3啊!
d p dp dp思路:(套用吴老师的经典语录) 如果一道题,既可以搜索,又看上去可以贪心,那么大部分就是 d p dp dp( r s j rsj rsj追问:那么那一小部分呢?吴老师:那就是贪心!) ( r s j rsj rsj反驳:为什么是我!除了 r s j rsj rsj的同学:就是你!) 我们设 f i , j , k f_{i,j,k} fi,j,k为第 i i i天买到第 j j j件而花了 k k k元钱所赚的钱。那么状态转移方程可以想象: f i , j , k = m a x ( f i , j , k , f i , j , k − a i , j + a i , j − a i − 1 , j f_{i,j,k}=max(f_{i,j,k},f_{i,j,k-a_{i,j}}+a_{i,j}-a_{i-1,j} fi,j,k=max(fi,j,k,fi,j,kai,j+ai,jai1,j;因为这是一个完全背包,所以我们可以正序枚举。而同样,我们注意到 f 100 , 100 , 10000 f_{100,100,10000} f100,100,10000会爆内存,所以我们可以滚动。所以状态转移方程可以改为: f i , k = m a x ( f i , k , f i , k − a i , j + a i , j − a i − 1 , j f_{i,k}=max(f_{i,k},f_{i,k-a_{i,j}}+a_{i,j}-a_{i-1,j} fi,k=max(fi,k,fi,kai,j+ai,jai1,j,而如果 a i , j ≤ a i − 1 , j a_{i,j}\leq a_{i-1,j} ai,jai1,j,那么就没有意义了。所以我们可以特判一下,卡个常(这只是最初级的)。
代码君奉上:

#include<cstdio>
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
int n,m,t,a[139][139],f[139][10039],ans,tot;
int main() {
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    register int i,j,k;
    scanf("%d%d%d",&t,&n,&m);
    for(i=1; i<=t; i++) {
        for(j=1; j<=n; j++) scanf("%d",&a[i][j]);
    }
    ans=m;
    for(i=1; i<t; i++) {
        tot=0;
        for(j=1; j<=n; j++) {
            if(a[i][j]<a[i+1][j]) {
                for(k=a[i][j]; k<=ans; k++) {
                    f[i][k]=max(f[i][k],f[i][k-a[i][j]]+a[i+1][j]-a[i][j]);
                    tot=max(tot,f[i][k]);
                }
            }

        }
        ans+=tot;
    }
    printf("%d",ans);
    return 0;
}

但是…原题很卡常。于是我们可以稍微的玩一玩。
我们观察到第十九行:

f[i][k]=max(f[i][k],f[i][k-a[i][j]]+a[i+1][j]-a[i][j]);
tot=max(tot,f[i][k]);

k − a i , j k-a_{i,j} kai,j十分耗时间,于是我们可以提前计算。 a i + 1 , j − a i , j a_{i+1,j}-a_{i,j} ai+1,jai,j也慢,于是也可以提前计算。而 t o t tot tot的取值也在重复计算,于是我们可以把它拉到外面来一次。
终极代码如下:

#include<cstdio>
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
int n,m,t,a[139][139],f[139][10039];
inline void read(register int &x){
    x=0;register int s=getchar();
    while(s<'0'||s>'9') s=getchar();
    while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+(s^48),s=getchar();
}
int main() {
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    register int i,j,k,tot,ans=0,now,pus;
    scanf("%d%d%d",&t,&n,&m);
    for(i=1; i<=t; i++) {
        for(j=1; j<=n; j++) read(a[i][j]);
    }
    ans=m;
    for(i=1; i<t; i++) {
        tot=0;
        for(j=1; j<=n; j++) {
            if(a[i][j]<a[i+1][j]) {
                now=a[i+1][j]-a[i][j];pus=0;
                for(k=a[i][j]; k<=ans; k++) {
                    f[i][k]=max(f[i][k],f[i][pus]+now);
                    pus++;
                }
            }
        }
        for(j=0;j<=ans;j++) tot=max(tot,f[i][j]);
        ans+=tot;
    }
    printf("%d",ans);
    return 0;
}

第一次提交记录
而改良后的提交记录
你是不是觉得看不出差异,那么看一看原题:第一次提交记录
第二次提交记录
看出差异了吧!

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值