好烦啊为什么不早点写博客懒死我算了啊好烦每天少睡一小时

采药(01背包

luogu P1048
在这里插入图片描述
背包动规模板题
01背包问题

LL dp[maxn],w[maxn],t[maxn];
LL tt,m;
int main(){
    tt=lrd();
    m=lrd();
    for(int i=1;i<=m;i++){
        t[i]=lrd();
        w[i]=lrd();
    }
    for(int i=1;i<=m;i++){
        for(int j=tt;j>=t[i];j--)
            dp[j]=max(dp[j],dp[j-t[i]]+w[i]);
    }
    cout<<dp[tt]<<endl;
    return 0;
}

疯狂的采药(完全背包

luogu P1616
在这里插入图片描述

完全背包模板

LL dp[maxn],w[maxn],t[maxn];
LL tt,m;
int main(){
    tt=lrd();
    m=lrd();
    for(int i=1;i<=m;i++){
        t[i]=lrd();
        w[i]=lrd();
    }
    for(int i=1;i<=m;i++){
        for(int j=t[i];j<=tt;j++)//01背包区别
            dp[j]=max(dp[j],dp[j-t[i]]+w[i]);
    }
    cout<<dp[tt]<<endl;
    return 0;
}

金明的预算方案(依赖背包

luogu P1064
在这里插入图片描述

四层dp状态转移,依赖背包
第0层表示所含附件的个数(主件为1),第一层代表主件,以后为附件1、2。购买附件的代价需要预处理加上主件的代价。
附件的购买情况分为三种:

  1. 只购买附件1
  2. 只购买附件2
  3. 附件1与附件二一起购买
LL dp[maxn],w[maxn][10],v[maxn][10];
LL n,m;
int main(){
    n=lrd();
    m=lrd();
    for(int i=1;i<=m;i++){
        LL a,b,c;
        a=lrd();
        b=lrd();
        c=lrd();
        if(c==0){//主件
            w[i][1]=a;
            v[i][1]=b*a;
        }
        else{//附件
            w[c][++w[c][0]+1]=a;
            v[c][++v[c][0]+1]=a*b;
            if(w[c][0]==2){
                w[c][w[c][0]+1]=w[c][2]+w[c][3];
                v[c][v[c][0]+1]=v[c][2]+v[c][3];
            }
        }
    }
    for(int i=1;i<=m;i++){//预处理
        if(w[i][0]!=0){
            w[i][2]+=w[i][1];
            v[i][2]+=v[i][1];
            if(w[i][0]==2){
                w[i][3]+=w[i][1];
                v[i][3]+=v[i][1];
                w[i][4]+=w[i][1];
                v[i][4]+=v[i][1];
            }
        }
    }
    for(int i=1;i<=m;i++){
        for(int j=n;j>=w[i][1];j--){
            dp[j]=max(dp[j],dp[j-w[i][1]]+v[i][1]);
            if(j>=w[i][2])
                dp[j]=max(dp[j],dp[j-w[i][2]]+v[i][2]);
            if(j>=w[i][3])
                dp[j]=max(dp[j],dp[j-w[i][3]]+v[i][3]);
            if(j>=w[i][4])
                dp[j]=max(dp[j],dp[j-w[i][4]]+v[i][4]);
        }
    }
    cout<<dp[n]<<endl;
    return 0;
}


数的划分 P1025

luogu P1025
luogu

忘记了就去看题解,下一个

int dp[220][10],n,k;
int main(){
    n=ird();
    k=ird();
    memset(dp,0,sizeof dp);
    for(int i=1;i<=n;i++){
        dp[i][1]=1;
    }
    for(int i=2;i<=n;i++){
        for(int j=2;j<=k;j++){
                if(j>i)
                    dp[i][j]=0;
                else
                    dp[i][j]=dp[i-1][j-1]+dp[i-j][j];
        }
    }
    cout<<dp[n][k]<<endl;
}

多米诺骨牌

luogu P1282
在这里插入图片描述

背包问题拓展

  • 转换为背包,首次反转贡献为-1
LL a[maxn][5],w[maxn],v[maxn],dp[maxn*6][maxn],vis[maxn*6][maxn];
LL n,m,ans=0;
int main(){
    n=lrd();
    m=0;
    for(int i=1;i<=n;i++){
        a[i][1]=lrd();
        a[i][2]=lrd();
        if(a[i][1]<a[i][2]){
            ans++;
            swap(a[i][1],a[i][2]);
            v[i]=-1;
        }
        else if(a[i][1]==a[i][2])
            v[i]=0;
        else
            v[i]=1;
        w[i]=a[i][1]-a[i][2];
        m+=w[i];
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            dp[j][i]=dp[j][i-1];
            vis[j][i]=vis[j][i-1];
            if(j>=2*w[i]){
                if(vis[j-2*w[i]][i-1]||j==2*w[i]){
                    if(vis[j][i]==0){
                        dp[j][i]=dp[j-2*w[i]][i-1]+v[i];
                        vis[j][i]=1;
                    }
                    else
                        dp[j][i]=min(dp[j][i],dp[j-2*w[i]][i-1]+v[i]);
                }
            }
        }
    }
    int f;
    for(f=m;f>=1;f--){
        if(vis[f][n]==1)
            break;
    }
    cout<<ans+dp[f][n]<<endl;
}


石子合并(模板

luogu P1880
在这里插入图片描述
模板题
dp[i][j]表示第i到第j堆的合并

输入从1-n*2实现一圈循环,dp时固定i,遍历ij之间中间划分块。
最后ans枚举起始点i从1-n,所有石子都要合成一堆

int a[maxn],sum[maxn];
int dpi[maxn][maxn],dpa[maxn][maxn];
int main(){
    int n=ird();
    for(int i=1;i<=n*2;i++){
        if(i<=n) a[i]=ird();
        a[i+n]=a[i];
        sum[i]=sum[i-1]+a[i];
    }
    for(int d=1;d<n;d++){
        for(int i=1,j=i+d;j<n*2&&i<n*2;i++,j=i+d){
            dpi[i][j]=1e9;
            for(int k=i;k<j;k++){
                dpi[i][j]=min(dpi[i][j],dpi[i][k]+dpi[k+1][j]+sum[j]-sum[i-1]);
                dpa[i][j]=max(dpa[i][j],dpa[i][k]+dpa[k+1][j]+sum[j]-sum[i-1]);
            }
        }
    }
    int ans=0,anss=1e8;
    for(int i=1;i<=n;i++){
        anss=min(anss,dpi[i][i+n-1]);
        ans=max(ans,dpa[i][i+n-1]);
    }
    cout<<anss<<endl<<ans<<endl;
    return 0;
}


能量项链

luogu P1063
在这里插入图片描述

同石子合并

int a[maxn],dp[maxn][maxn];
struct node{
    int h,t;
}mp[maxn];
int n,m;
int main(){
    n=ird();
    for(int i=1;i<=n;i++){
        a[i]=ird();
    }
    for(int i=1;i<=n;i++){
        mp[i].h=a[i];
        mp[i+n].h=a[i];
        if(i==n){
            mp[i].t=a[1];
        }
        else mp[i].t=a[i+1];
        mp[i+n].t=mp[i].t;
    }
    for(int d=1;d<n;d++){
        for(int i=1,j=i+d;i<=n*2&&j<=n*2;i++,j=i+d){
            for(int k=i;k<j;k++){
                dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+mp[i].h*mp[k].t*mp[j].t);
            }
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        ans=max(ans,dp[i][i+n-1]);
    }
    cout<<ans<<endl;
    return 0;
}


最大正方形(线性dp 1

在这里插入图片描述
dp[i][j]代表(i,j)为右下角的正方形的最大边长

  • 转移方程:
    • dp[i][j]=min(min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1**
      
int mp[maxn][maxn],dp[maxn][maxn];
int n,m;
int main(){
    n=ird();
    m=ird();
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            mp[i][j]=ird();
            if(mp[i][j])
                dp[i][j]=1;
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(mp[i][j]) dp[i][j]=min(min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1;
            ans=max(ans,dp[i][j]);
        }
    }
    cout<<ans<<endl;
    return 0;
}

创意吃鱼法 (线性dp2

luogu P1736
在这里插入图片描述
和上一题类似,多了一个预处理

  • 处理以(i,j)为起点,向上、左、右三个方向扩展连续的0的最大长度
  • mz数组记录左右拓展最大长度(前缀和),ms数组记录向上最大连续0的长度。
  • 因为对角线有俩,第一遍统计向左的。mz记录👈最大值。第二遍统计向右的,mz记录👉最大值 (or MLE)
  • 每次遍历ans比较记录最大值。
int mp[maxn][maxn],mz[maxn][maxn],ms[maxn][maxn],dp[maxn][maxn];
int n,m;
int main(){
    n=ird();
    m=ird();
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            mp[i][j]=ird();
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(!mp[i][j])
                mz[i][j]=mz[i][j-1]+1;
        }
    }
    for(int j=1;j<=m;j++){
        for(int i=1;i<=n;i++){
            if(!mp[i][j])
                ms[i][j]=ms[i-1][j]+1;
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(mp[i][j]){
                if(dp[i-1][j-1]>=1){
                    dp[i][j]=min(min(mz[i][j-1],ms[i-1][j]),dp[i-1][j-1])+1;
                }
                else
                    dp[i][j]=1;
                ans=max(ans,dp[i][j]);
            }
        }
    }
    memset(dp,0,sizeof dp);
    for(int i=1;i<=n;i++){
        for(int j=m;j>=1;j--){
            if(!mp[i][j])
                mz[i][j]=mz[i][j+1]+1;
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(mp[i][j]){
                if(dp[i-1][j+1]>=1){
                    dp[i][j]=min(min(mz[i][j+1],ms[i-1][j]),dp[i-1][j+1])+1;
                }
                else
                    dp[i][j]=1;
                ans=max(ans,dp[i][j]);
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值