(Day13) 算法复健运动for蓝桥杯-背包

(Day13) 算法复健运动for蓝桥杯-背包

01背包:

https://www.acwing.com/problem/content/2/

倒着循环,保证每个只用一次

for(int i=1;i<=n;i++)
{
    cin>>v>>w;
    for(int j=t;j>=v;j--)
    {
        dp[j]=max(dp[j],dp[j-v]+w);
    }
}

换容量的01背包:
见动态规划笔记本里面有篇叫“改容量的01背包”

完全背包:

https://www.acwing.com/problem/content/3/

正着循环,每个可以使用多次

for(int i=1;i<=n;i++)
{
    cin>>v>>w;
    for(int j=v;j<=t;j++)
    {
        dp[j]=max(dp[j],dp[j-v]+w);
    }
}

多重背包:

https://www.acwing.com/problem/content/6/

每个物品有他的数量,

普通的多重背包:

#include<bits/stdc++.h>
using namespace std;
const int N=109;
int v[N];//体积
int w[N];//价值
int s[N];//数量
int dp[N];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);//种数和容积
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d",&v[i],&w[i],&s[i]);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=m;j>=0;j--)
        {
            for(int k=0;k<=s[i]&&k*v[i]<=j;k++)
            {
                dp[j]=max(dp[j],dp[j-k*v[i]]+k*w[i]);
            }
        }
    }
    printf("%d\n",dp[m]);
    return 0;
}

二进制优化的多重背包:

原理:生成的每个k和剩下的s[i]可以用来表示个数范围中的所有个数,所以用这些数去跑多重背包会减少时间。

#include<bits/stdc++.h>
using namespace std;
const int N=2009;
int v[N];//体积
int w[N];//价值
int s[N];//数量
int dp[N];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);//种数和容积
    for(int i=1; i<=n; i++)
    {
        scanf("%d%d%d",&v[i],&w[i],&s[i]);
    }
    for(int i=1; i<=n; i++)
    {
        for(int k=1; k<=s[i]; k*=2)
        {
            s[i]-=k;
            for(int j=m;j>=k*v[i];j--)
            {
                dp[j]=max(dp[j],dp[j-k*v[i]]+k*w[i]);
            }
        }
        if(s[i]>0)
        {
            for(int j=m;j>=s[i]*v[i];j--)
            {
                dp[j]=max(dp[j],dp[j-s[i]*v[i]]+s[i]*w[i]);
            }
        }
    }
    printf("%d\n",dp[m]);
    return 0;
}

单调队列优化的多重背包:

#include<bits/stdc++.h>
using namespace std;

const int N=20009;

int n,m;
int f[N],g[N],q[N];

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++)
    {
        int v,w,s;
        scanf("%d%d%d",&v,&w,&s);
        memcpy(g,f,sizeof(f));
        for(int j=0;j<v;j++)
        {
            int hh=0,tt=-1;
            for(int k=j;k<=m;k+=v)
            {
                f[k]=g[k];
                if(hh<=tt&&k-s*v>q[hh])hh++;
                if(hh<=tt)f[k]=max(f[k],g[q[hh]]+(k-q[hh])/v*w);
                while(hh<=tt&&g[q[tt]]-(q[tt]-j)/v*w<=g[k]-(k-j)/v*w)tt--;
                q[++tt]=k;
            }
        }
    }
    printf("%d\n",f[m]);
    return 0;
}

例题:luogu P1504 积木城堡

https://www.luogu.com.cn/problem/P1504

首先得理解题意,已有的积木不能重新组合,必须只能抽出来。

思路是去单独把每一个城堡跑一次dp,看看哪些高度能组出来,vis[i]为01数组表示高度为i的城堡是否能组出来,然后把每个城堡的状态加起来,如果dp[j]==n,说明这个高度每个城堡都可以组成,所以从大往小遍历第一个=n的就满足,为了顺应题目要求vis[0]=1。遍历必须遍历到0

#include<bits/stdc++.h>
using namespace std;
const int N = 1e4+9;
int n,x;
int dp[N],vis[N];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        memset(vis,0,sizeof(vis));
        vector<int>ve;
        while(cin>>x&&x!=-1)
        {
            ve.push_back(x);
        }
        int len=ve.size();
        vis[0]=1;
        for(int j=0;j<len;j++)
        {
            for(int k=N;k>=ve[j];k--)
            {
                if(vis[k-ve[j]])vis[k]=1;
            }
        }
        for(int j=0;j<=N;j++)
        {
            dp[j]+=vis[j];
        }
    }
    for(int i=N;i>=0;i--)
    {
        if(dp[i]==n)
        {
            cout<<i<<endl;
            break;
        }
    }
    return 0;
}
posted @   wlqtc  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示