首页 > 其他分享 >背包问题变式总结

背包问题变式总结

时间:2023-08-13 16:34:42浏览次数:50  
标签:总结 背包 变式 int TS cin 01 include

01背包

01背包完全装满求方案数

Acwing 278 数字组合

状态表示:二维

集合:所有从前 \(i\) 个数里面选,且和是 \(j\) 的选法的集合

属性:选法的数量

状态计算 分为 选 \(i\) 的所有方案 和 不选 \(i\) 的所有方案

不选 \(i\) 也就是从前 \(i-1\) 个数里面选,且和是 \(j\) 的方案数 \(f[i-1,j]\)

选 \(i\) 也就是从前 \(i-1\) 个数里面选,且和是 \(j-a_i\) 的方案数,注意是 += ,因为我们算的是数量而不是最大值

注意当 \(j=0\) 也是有一种方案的,要初始化为 \(1\)

#include <iostream>
using namespace std;
const int N = 105, M = 10005;
int f[N][M], a[N];
int n,m;
int main()
{
    cin>>n>>m;
    f[0][0]=1;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        f[i][0]=1;
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            f[i][j]+=f[i-1][j];
            if(j>=a[i]) f[i][j]+=f[i-1][j-a[i]];
        }
    }
    cout<<f[n][m];
    return 0;
}

优化也很简单,参考01背包的优化方式

这里不过多赘述,上代码

#include <iostream>
using namespace std;
const int N = 10005;
int f[N];
int main()
{
    int n,m;
    cin>>n>>m;
    f[0]=1;
    for(int i=1;i<=n;i++)
    {
        int a;
        cin>>a;
        for(int j=m;j>=a;j--)
            f[j]+=f[j-a];
    }
    cout<<f[m];
    return 0;
}

01背包平分子集

这个标题有些迷惑,什么是平分子集,有什么实际运用吗?

平分子集大体意思是给你几个数,把这些数分成两组,问你最小的差值是多少?

我们可以将数的总和记录下来,然后将这个数除以2,再从数组里面挑数字尽可能装满这个背包

解释一下:想要两个子集差值最小,那么这个值最小为0,也就是相等,那么我们就尽可能选出一些数字接近这个一半,越接近差值就越小

首先来一道比较裸的题 巧分配

只需要按照上文说的方法再套上01背包的模板就行了,不过多解释

#include <iostream>
#include <cstring>
using namespace std;
const int N = 50005;
int a[N];
int main()
{
    int n,t;
    cin>>t;
    while(t--)
    {
        int f[N]={0};
        cin>>n;
        int sum=0;
        for(int i=1;i<=n;i++) cin>>a[i],sum+=a[i];
        for(int i=1;i<=n;i++)
            for(int j=sum/2;j>=a[i];j--)
                f[j]=max(f[j],f[j-a[i]]+a[i]);
        cout<<sum-f[sum/2]-f[sum/2]<<endl;
    }
    return 0;
}

来一个进阶版本 kkksc03考前临时抱佛脚

题目中说的有4门学科容易误导,让人认为是分组背包什么的,实际上只相当于多组测试数据罢了,那么我们就把目光聚焦到一组上面

我们可以发现可以同时解决两个问题,那么想要这个特性运用的最划算,那么肯定是需要左脑和右脑处理的时间的差值最小,那么这个问题就转换成了平分子集问题,不过需要注意的是,这里求的是总时间,因为左脑和右脑只能处理同一科,所以如果左脑处理完了,左脑还得等右脑,所以需要取一个最大值,最后将每次取得的结果累加就行了

#include <iostream>
#include <cstring>
using namespace std;
const int N = 1005;
int s[5];
int f[N],a[N];
int main()
{
    int res=0;
    for(int i=1;i<=4;i++) cin>>s[i];
    for(int t=1;t<=4;t++)
    {
        memset(f,0,sizeof f);
        int sum=0;
        for(int i=1;i<=s[t];i++) cin>>a[i],sum+=a[i];
        for(int i=1;i<=s[t];i++)
            for(int j=sum/2;j>=a[i];j--)
                f[j]=max(f[j],f[j-a[i]]+a[i]);
        res+=max(sum-f[sum/2],f[sum/2]);
    }
    cout<<res;
    return 0;
}

含有负数的01背包

[USACO03FALL]Cow Exhibition G 奶牛博览会

题目要求两个指数不能是负数,且和最大,不能看作简单的01背包来处理

我们可以设计一下状态,假设 \(TS\) 为正数(弄一个偏移量,把负数变成正数)。状态 \(f[i]\) 表示花费 \(i\) 的聪明指数能得到的最大的风趣指数

这里有几个问题需要注意一下

  1. 初始化数组为负无穷,因为数据中是有正数的,其次初始化 \(f[m]\) 为0,表示当 TS 的值达到 0 的时候,TF 的最大值为 0
  2. 我们的偏移量要是数据上限的两倍,因为枚举时出现了 \(-v[i]\)
  3. 负数与正数要区别对待

正数和负数为什么会不一样?

在正数中,\(j > j-v[i]\) ,我们想要求得 \(f[j]\) 的话就必须要使用 \(i-1\) 层的 \(f[j-TS[i]]\) 所以如果从小到大的话会导致 \(f[j-TS[i]]\) 先算,这样用的就是 \(i\) 层的 \(f[j-TS[i]]\) 了,与原本二维的状态是不符的

而在负数中,\(j<j-TS[i]\) ,我们想要球的 \(f[j]\) 还是要用 \(i-1\) 层的 \(f[j-TS[i]]\),如果我们从大到小的话就会导致 \(f[j-TS[i]]\) 先算,与之前是同一个道理,所以需要区别对待

#include <iostream>
#include <cstring>
using namespace std;
const int N = 105, M = 200005;
int f[M],TS[N],TF[N]; //当 TS 的值达到 i 的时候,TF 的最大值
int main()
{
    int n,m=0;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>TS[i]>>TF[i];
        if(TS[i]>0) m+=TS[i];
    }
    memset(f,-0x3f,sizeof f);
    f[m]=0;
    for(int i=1;i<=n;i++)
    {
        //正数的情况
        if(TS[i]>0)
            for(int j=m*2;j>=TS[i];j--)
                f[j]=max(f[j],f[j-TS[i]]+TF[i]);
        //负数的情况
        else
            for(int j=0;j<=m*2+TS[i];j++)
                f[j]=max(f[j],f[j-TS[i]]+TF[i]);
    }
    int ans=0;
    for(int i=0;i<=m;i++)
    {
        if(f[i+m]>0) ans=max(ans,f[i+m]+i);
    }
    cout<<ans;
    return 0;
}

完全背包

完全背包完全装满求方案数

由于完全背包的最终版本和01背包非常像,这里就不多赘述,具体看上文提到的01背包完全装满求方案数

#include <iostream>
using namespace std;
const int N = 10005;
int f[N];
int main()
{
    int n,m;
    cin>>n>>m;
    f[0]=1;
    for(int i=1;i<=n;i++)
    {
        int a; 
        cin>>a;
        for(int j=a;j<=m;j++)
            f[j]+=f[j-a];
    }
    cout<<f[m];
    return 0;
}

进阶版本 NOIP2018 提高组 货币系统

由于纸币有无穷张,所以是完全背包

首先来解释一下题目是什么意思,题目要求我们设计一个等价的货币系统,相当于给出的货币系统有的面值是没有用的,我们将这个货币系统优化一下,去掉几种面值,但是不能多表示一些面值,也不能少表示一些面值

那我们可以将现有的货币系统能表示的所有面值都搞出来,然后看需要什么面值的钱才能表示这个面值

换句话讲,例如说如果 5 这个面值 只有一种表示方法,那么一定是1张5元的钱,那么这张钱就不能省去,是必要的

如果有两种表示方法的话,那么就可以被替代了,由于面值只能相加不能相减,所以这两种方法其中一定有一种是由比这张钱价值小的钱凑出来的,所以我们就不需要新的钱了

另外有可能对无穷张这个点有些疑惑,无穷张不管是 2张 还是 3张 表示的面值也一定用给出的货币系统的面值凑出来,所以无需考虑

#include <iostream>
#include <cstring>
using namespace std;
const int N = 25005;
int f[N],a[105];
int main()
{
    int T; cin>>T;
    while(T--)
    {
        memset(a,0,sizeof a); memset(f,0,sizeof f);
        int maxx=0,ans=0;
        int n; cin>>n;
        f[0]=1;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
            maxx=max(maxx,a[i]);
        }
        for(int i=1;i<=n;i++)
            for(int j=a[i];j<=maxx;j++)
                f[j]+=f[j-a[i]];
        for(int i=1;i<=n;i++)
            if(f[a[i]]==1) ans++;
        cout<<ans<<endl;
    }
    return 0;
}

分组背包

分组背包完全装满求方案数

摆花

与之前的一样,不过要注意的是,分组里面枚举的 \(k\) 是下标,而这里的体积都为1,所以说如果从 \(0\) 开始枚举的话要 \(-(k+1)\)

#include <iostream>
using namespace std;
const int N = 1005;
int f[N];
int main()
{
    int n,m;
    cin>>n>>m;
    f[0]=1;
    for(int i=1;i<=n;i++)
    {
        int a; cin>>a;
        for(int j=m;j>=0;j--)
        {
            for(int k=0;k<a;k++)
            {
                if(k<=j) f[j]=(f[j]+f[j-k-1])%1000007;
            }
        }
    }
    cout<<f[m]%1000007;
    return 0;
}

标签:总结,背包,变式,int,TS,cin,01,include
From: https://www.cnblogs.com/xiaozhu0602/p/17626708.html

相关文章

  • svg foreignObject 作用总结
    svgforeignObject主要能实现文本换行和dom转图片两个功能1.svg文本换行svg文字功能很弱,不支持自动换行,需要用tspan进行截断<svgxmlns="http://www.w3.org/2000/svg"><textfont-size="16"><tspanx="0"y="10">这个是一段要换</tspan>......
  • 第五周总结
    周一:明确学习目标和资源准备本周的第一天,我将明确自己的学习目标,确定学习重点和方向。我计划阅读相关的大数据领域的书籍和文档,如《大数据导论》、《Hadoop权威指南》等,以了解各种大数据技术和工具的基本概念和应用场景。此外,我也会寻找一些在线学习资源和教程,如网课、博客和视频......
  • 周总结5
    1、java的三大平台分别为javaSE、javaEE、javaME,其中javaSE是基础。2、javaSE由四部分组成,分别为JVM、JRE、JDK、java语言。3、java不仅是一门程序语言,还是一种标准,我认为,这种标准体现在它可以JYM平台实现跨平台的功能。JCP是一个组织,JSR为一种提交文件的方式,其目的可以理......
  • 背包问题基础模型全解
    01背包Acwing2.01背包问题状态表示:二维集合:只从前\(i\)个物品里面选择总体积\(\leqj\)选法的集合属性:选法价值的最大值状态计算分为放\(i\)和不放\(i\)(要不要把当前物品放进背包):不放\(i\)意味着在前\(i-1\)个物品里面选,且总体积不超过\(j\)放\(i\)......
  • SpringMVC总结
    SpringMVC:Web层框架 @RestControll @Controller实例化对象,并添加到容器 @ResponseBody将返回结果转换为JSON格式 @RequestMapping(value="url可以定义多个",method=POST|GET)映射请求地址 value映射地址可以定义多个 method如果不写,则默认匹配所有请求方式。 @RequestPara......
  • 8月5号到8月12号。假期周总结
    8月5号到8月12号。周日我看了一下Python的课程二进制编码,标识符保留四变量的定义使用,还有给变量赋值数据的类型。周一我学习了Python的输入输出运算符的使用方法,有运算符的优先级。还有分支结构,单分支结构,双分支结构,多分支结构,嵌套的分支结构。我还学习了一下条件表达式。周二我......
  • 背包
    01背包给定\(n\)件物品,每个物品有重量\(w_{i}\)和价值\(c_{i}\),一个物品只有一件,求容量不超过\(m\)的背包最多可以装多少价值物品定义\(f_{i,j}\)表示前\(i\)件物品在容量不超过\(j\)的背包下可以获得的最大价值则有\(f_{i,j}=\max\{f_{i-1,j},f_{i-1,j-w_{i}}+c_......
  • 暑假第六周总结
    在本周,我学习的内容很少,因为我在本周选择了出去旅行放松身心。在这一周中,我带着我的弟弟去了北京。这是我半年前答应好他的,在这个暑假我来实现这个承诺。说是带他去北京,但是这也是我为数不多去北京的机会,之前因为年纪小去过的景点都有些忘记,这一次去又是一种全新的体验......
  • 第七周训练总结
    比赛总结牛客多校第七场1/2/13AC:M补题:C总结:开题445,各自读题,wyf读完M后发现是签到题,开始写M。第一发int溢出wa了,后来改longlong通过。然后榜上C和I题过的较多,先集中想的I题,很快出了一个思路,但是在构思如何实现的时候发现思路是错误的,觉得I题的求解比较麻烦,放掉。然后三个......
  • 8.12-晚上阵列总结
    第一种情况:物体的长是19.5宽是32.72x方向间距为15y方向间距为30  第二种情况 第三种情况 ......