目录
介绍:
一、01背包
题目描述
输入描述:
输出描述:
代码:
二、完全背包
题目描述
输入描述:
输出描述:
代码:
三、多重背包
题目描述
输入描述:
输出描述:
代码:
四、背包问题
题目描述
输入描述:
输出描述:
代码:
五、变1
代码:
六、变2
代码:
七、变3
代码:
介绍:
背包问题是一个经典的组合优化问题,它的目标是在给定的一组物品中选择一些物品放入背包中,使得这些物品的总价值最大化,同时限制背包的容量。
背包问题有多种变体,其中最常见的是0-1背包问题。在0-1背包问题中,每个物品要么完整地放入背包中,要么不放入,不存在放入一部分的情况。每个物品都有对应的价值和重量,背包有一个固定的容量限制。问题在于选择哪些物品放入背包中,使得总价值最大化,同时不超过背包的容量。
解决背包问题的方法包括动态规划、贪心法和回溯法。动态规划是最常用的解决方法,其基本思想是将问题划分为子问题,并使用一个二维数组来保存子问题的最优解。贪心法则是每次选择当前最有利的物品放入背包,但不一定能得到最优解。回溯法则是穷举所有可能的解,并逐步剪枝,但在处理大规模问题时效率较低。
背包问题具有广泛的应用,例如资源分配、排课问题、投资组合优化等。
一、01背包
题目描述
你有一个背包,最多能容纳的体积是V。
现在有n个物品,第i个物品的体积为v ,价值为w。
(1)求这个背包至多能装多大价值的物品?
(2)若背包恰好装满,求至多能装多大价值的物品?
输入描述:
第一行两个整数n和V,表示物品个数和背包体积。
接下来n行,每行两个数v和w,表示第i个物品的体积和价值。
1≤n,V,vi,wi≤10001 \le n,V,v_i,w_i \le 10001≤n,V,vi,wi≤1000
输出描述:
输出有两行,第一行输出第一问的答案,第二行输出第二问的答案,如果无解请输出0。
代码:
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
int n, v;
cin >> n >> v;
int a[1005],b[1005];
int f[1005][1005];//表示第i个物品装满j体积
memset(f, -0x3f, sizeof(f));
for (int i = 1; i <= n; i++)
cin >> a[i] >> b[i];
int ans = 0;
f[0][0]=0;//0个物体可以装满0体积
for(int i=1;i<=n;i++)
for (int j = 0; j <= v; j++)
{
if(a[i]<=j)//体积大于该物体体积,才可以装物品
f[i][j] = max(f[i - 1][j], f[i - 1][j - a[i]] + b[i]);
else//不装该物体
f[i][j]=f[i-1][j];
if (ans < f[i][j])//求最大价值
ans = f[i][j];
}
cout << ans << endl;
if(f[n][v]>0)
cout << f[n][v] << endl;
else
cout<<0;
}
二、完全背包
题目描述
你有一个背包,最多能容纳的体积是V。
现在有n种物品,每种物品有任意多个,第i种物品的体积为vi ,价值为wi。
(1)求这个背包至多能装多大价值的物品?
(2)若背包恰好装满,求至多能装多大价值的物品?
输入描述:
第一行两个整数n和V,表示物品个数和背包体积。
接下来n行,每行两个数v和w,表示第i种物品的体积和价值。
1≤n,V≤10001 \le n, V \le 10001≤n,V≤1000
输出描述:
输出有两行,第一行输出第一问的答案,第二行输出第二问的答案,如果无解请输出0。
代码:
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
long long n, V;
cin >> n >> V;
long long dp1[1010], dp2[1010];
memset(dp1, 0, sizeof(dp1)), memset(dp2, 0, sizeof(dp2));
for (int i = 1; i <= n; i++)
{
long long v, w;
cin >> v >> w;
for (int j = 0; j <= V; j++)
{
if (j >= v)
{
dp1[j] = max(dp1[j], dp1[j - v] + w);
if (j % v == 0 || dp2[j-v] != 0) //该体积为该物体体积的倍数或者该体积减去物体体积不为0
dp2[j] = max(dp2[j], dp2[j - v] + w);//塞这个物品,原先该体积的价值和减去该体积的价值加塞下该物品的价值谁大
}
}
}
cout << dp1[V] << endl;
cout << dp2[V] << endl;
}
三、多重背包
题目描述
有 n 种物品,第 i 种物品有 x 个,每一个物品重量为 w ,价值为 v,现有一个承重能力为 T 的背包,在不超过承重能力的情况下,背包种最多能装多少价值的物品。
输入描述:
第一行输入两个整数 n,T(1≤n,T≤100)n,T(1\le n,T \le 100)n,T(1≤n,T≤100) ,代表物品种数和背包承重能力。
接下来 nnn 行,每行三个整数 xi,wi,vi(1≤xi,wi≤20,1≤vi≤200)x_i,w_i,v_i(1\le x_i,w_i\le 20,1\le v_i \le 200)xi,wi,vi(1≤xi,wi≤20,1≤vi≤200) 描述一个物品,分别代表物品的个数、物品的重量、物品的价值。
输出描述:
输出一行一个整数,表示在不超过承重能力的情况下,背包物品的最大价值。
代码:
#include<iostream>
using namespace std;
int main()
{
int n, t;
cin >> n >> t;
int f[105][105];//表示第i个物品装j体积
for (int i = 1; i <= n; i++)
{
int x, w, v;
cin >> x >> w >> v;
for (int j = 0; j <= t; j++)
{
for (int k = 0; k <= x; k++)//表示放k个该物品
if(j>=k*w)//要该体积大于k个物品体积才能放那么多个物品
f[i][j] = max(f[i][j], f[i - 1][j - k * w] + k*v);
}
}
cout << f[n][t];
}
四、背包问题
题目描述
牛妹家里有n个物品,第i个物品体积为v,价值为w。牛妹有一个容积无穷大的背包,背包可以装任意多的物品,没有限制。
牛妹要去深山中度假,为了在旁人眼中显得自己准备得很充分,牛妹想在背包中装入总体积不小于V的一些物品。如果装入的物品过于贵重,路上如果遇到强盗、劫匪、山贼等就很亏。所以牛妹想知道总体积不小于V的前提下,物品的总价值最小是多少。输入描述:
第一行两个整数n,V\mathit n,Vn,V,表示物品的数量和总体积需求。接下来n\mathit nn行,每行两个整数vi,wiv_i,w_ivi,wi,表示第i\mathit ii种物品的体积和价值。保证所有物品总体积不小于V\mathit VV。
1≤n≤200,1≤V≤106,1≤vi≤106,1≤wi≤2001 \leq n \leq 200, 1 \leq V \leq 10^6, 1 \leq v_i \leq 10^6, 1 \leq w_i \leq 2001≤n≤200,1≤V≤106,1≤vi≤106,1≤wi≤200。保证所有物品总体积不小于V\mathit VV。
输出描述:
输出一个整数,表示答案。
代码:
#include<iostream>
using namespace std;
int v[1000100], w[1000100];
int f[1000100];//j价值可以装的最大体积
int main()
{
int n,V;
long long sum = 0;
cin >> n >> V;
for (int i = 1; i <= n; i++)
{
cin >> v[i] >> w[i];
sum += w[i];//总价值
}
for (int i = 1; i <= n; i++)
{
for (int j = sum; j >= w[i]; j--)
f[j] = max(f[j], f[j - w[i]] + v[i]);
}
for (int i = 1; i <= sum; i++)
if (f[i] >= V)//找到大于等于V体积的最小价值
{
cout << i << endl;
break;
}
}
五、变1
代码:
#include<iostream>
using namespace std;
int main()
{
int n, m;
cin >> n >> m;
int v[30], p[30];
for (int i = 1; i <= m; i++)
{
cin >> v[i] >> p[i];
}
long long dp[30010] = { 0 };//当存体积为j时的,价值最大值
for(int i=1;i<=m;i++)
for (int j = n; j >= v[i]; j--)//当存到第i个时,花费为j时价值的最大值
{
dp[j] = max(dp[j], dp[j - v[i]] + p[i] * v[i]);//不选取i则继承存到第i-1个时的最大值,选取i则最大值更新为减去第i个花费时的最大值dp加上第i个的价值,即dp[j - v[i]] + p[i] * v[i]
}
cout << dp[n] << endl;
}
六、变2
代码:
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
int t;
cin >> t;
while (t--)
{
int n, s;
cin >> n >> s;
long long f[500000], q[500000];//f记录j体积的最高战力,q记录j体积的精灵数量
memset(f, 0, sizeof(f)), memset(q, 0, sizeof(q));
for (int i = 1; i <= n; i++)
{
int w, v;
cin >> w >> v;
for (int j = s; j >= w; j--)
{
if (f[j] < f[j - w] + v)//装该精灵战力更高,则更新
{
f[j] = f[j - w] + v;
q[j] = q[j-w]+1;
}
}
}
cout << f[s] << " "<<q[s]<<endl;
}
}
七、变3
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int a[105], f[25005];//f[i]表示价值i是否可以表示
int main()
{
int t;
cin>>t;
while (t--)
{
int n;
cin >> n;
int ans = n;
for (int i = 1; i <= n; i++)
cin >> a[i];
sort(a + 1, a + n + 1);//从小到大排序
memset(f, 0, sizeof(f));//清空为0
f[0] = 1;
for (int i = 1; i <= n; i++)
{
if (f[a[i]] == 1)//a[i]该数值可以被之前表示,则ans--,这个票不需要用到
{
ans--;
continue;
}
for (int k = a[i]; k <= a[n]; k++)//a[i]该数值不可以被表示,则从该数值到最大值,将加上a[i]后可以表示的数赋值为1
{
f[k] = f[k] || f[k - a[i]];
}
}
cout << ans << endl;
}
}