背包

(一)01背包

洛谷P1049 [NOIP 2001 普及组] 装箱问题:
https://www.luogu.com.cn/problem/P1049

题目描述

有一个箱子容量为 V,同时有 n 个物品,每个物品有一个体积。

现在从 n 个物品中,任取若干个装入箱内(也可以不取),使箱子的剩余空间最小。输出这个最小值。

输入格式

第一行共一个整数 V,表示箱子容量。

第二行共一个整数 n,表示物品总数。

接下来 n 行,每行有一个正整数,表示第 i 个物品的体积。

输出格式

共一行一个整数,表示箱子最小剩余空间。

代码实现:

1. 二维

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

int x,f[32][20010];
int main(){
  int n,m;
  cin>>m>>n;
  for(int i=1;i<=n;i++){
    cin>>x;
    for(int j=0;j<=m;j++)
      f[i][j]=f[i-1][j];
    for(int j=x;j<=m;j++)
      f[i][j]=max(f[i-1][j],f[i-1][j-x]+x);
  }
  cout<<m-f[n][m];
  return 0;
} 

2. 一维

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

int x,f[20010];
int main(){
  int n,m;
  cin>>m>>n;
  for(int i=1;i<=n;i++){
    cin>>x;
    for(int j=m;j>=x;j--)
      f[j]=max(f[j],f[j-x]+x);
      //注意j从后往前循环,因为f具有后效性
  }
  cout<<m-f[m];

  return 0;
}

(二)完全背包

洛谷P1616 疯狂的采药:
https://www.luogu.com.cn/problem/P1616

题目描述

LiYuxiang 是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同种类的草药,采每一种都需要一些时间,每一种也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

如果你是 LiYuxiang,你能完成这个任务吗?

此题和原题的不同点:

  1. 每种草药可以无限制地疯狂采摘。

  2. 药的种类眼花缭乱,采药时间好长好长啊!师傅等得菊花都谢了!

输入格式

输入第一行有两个整数,分别代表总共能够用来采药的时间 t 和代表山洞里的草药的数目 m。

第 2 到第 (m+1) 行,每行两个整数,第 (i+1) 行的整数 ai​,bi​ 分别表示采摘第 i 种草药的时间和该草药的价值。

输出格式

输出一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

1. 二维

#include "bits/stdc++.h"
#define ll long long
using namespace std;

const int T=1e7+5,M=1e4+5;
ll t,m,a[M],b[M],f[T][T];
int main(){
  cin>>t>>m;
  for(int i=1;i<=m;i++){
    cin>>a[i]>>b[i];
    for(int j=a[i];j<=t;j++)
      f[i][j]=max(f[i-1][j],f[i-1][j-a[i]]+b[i]);
  }
  cout<<f[t];
  return 0;
}

2. 一维

#include "bits/stdc++.h"
#define ll long long
using namespace std;

const int T=1e7+5,M=1e4+5;
ll t,m,a[M],b[M],f[T];
int main(){
  cin>>t>>m;
  for(int i=1;i<=m;i++){
    cin>>a[i]>>b[i];
    for(int j=a[i];j<=t;j++)
      f[j]=max(f[j],f[j-a[i]]+b[i]);
  }
  cout<<f[t];
  return 0;
}

(三)二进制优化

以完全背包为例:洛谷P1776宝物筛选:
https://www.luogu.com.cn/problem/P1776

题目描述

终于,破解了千年的难题。小 FF 找到了王室的宝物室,里面堆满了无数价值连城的宝物。

这下小 FF 可发财了,嘎嘎。但是这里的宝物实在是太多了,小 FF 的采集车似乎装不下那么多宝物。看来小 FF 只能含泪舍弃其中的一部分宝物了。

小 FF 对洞穴里的宝物进行了整理,他发现每样宝物都有一件或者多件。他粗略估算了下每样宝物的价值,之后开始了宝物筛选工作:小 FF 有一个最大载重为 W 的采集车,洞穴里总共有 n 种宝物,每种宝物的价值为 vi​,重量为 wi​,每种宝物有 mi​ 件。小 FF 希望在采集车不超载的前提下,选择一些宝物装进采集车,使得它们的价值和最大。

输入格式

第一行为两个整数 n 和 W,分别表示宝物种数和采集车的最大载重。

接下来 n 行每行三个整数 vi​,wi​,mi​。

输出格式

输出仅一个整数,表示在采集车不超载的情况下收集的宝物的最大价值。

代码实现:

1. 一维二进制优化

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

const int N=1000010;
int n,m,x,y,z,cnt,f[N],w[N],v[N];
int main(){  
  scanf("%d%d",&n,&m);
  for(int i=1;i<=n;i++){
    scanf("%d%d%d",&x,&y,&z);
    for(int j=1;j<=z;j<<=1)v[++cnt]=x*j,w[cnt]=y*j,z-=j;
    if(z)v[++cnt]=x*z,w[cnt]=y*z;//差值
  }
  for(int i=1;i<=cnt;i++)
  for(int j=m;j>=w[i];j--)
    f[j]=max(f[j],f[j-w[i]]+v[i]);
  printf("%d\n",f[m]);
  return 0;
}

posted on 2025-04-14 20:04  小黄同学01  阅读(15)  评论(0)    收藏  举报

导航