题目总链接
01 背包问题
分析
- 题意
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 1 件物品的体积是 vi, 价值是 Wi
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数,N, V, 用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 Vi, Wi, 用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围 0 <N, VS 1000
0 < Vi, Wi S 1000
输入样例
45
12
24
34
45
- 分析:注意 dp 的方向和 dp 时候 for 循环的范围
代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;
const int mxn = 1e5;
int dp[mxn];
int main()
{
/* fre(); */
int n, m;
sc("%d %d", &n, &m);
int v, w;
for_(i, 1, n)
{
sc("%d %d", &v, &w);
rep_(j, m, v)
dp[j] = max(dp[j], dp[j - v] + w);
}
pr("%d\n", dp[m]);
return 0;
}
完全背包问题
分析
- 题意
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 Vi, 价值是 Wi
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数,N, V, 用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi, Wi, 用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围 0<N.V<10000<Vi, WiS 1000
输入样例
4 5
1 2
24
34
45
输出样例:8
- 分析
- 从后往前往 for 循环 dp,这样可以重复利用 当前正在讨论的物品
代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;
const int mxn = 1e5;
int dp[mxn];
int main()
{
/* fre(); */
int n, m;
sc("%d %d", &n, &m);
int v, w;
for_(i, 1, n)
{
sc("%d %d", &v, &w);
for_(j, v, m)
dp[j] = max(dp[j], dp[j - v] + w);
}
pr("%d\n", dp[m]);
return 0;
}
多重背包问题 I
分析
- 题意
有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 s; 件,每件体积是 Vi, 价值是 Wi
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N, V, 用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 Vi, Wi, Si, 用空格隔开,分别表示第 1 种物品的体积、价值和数量
输出格式
输出一个整数,表示最大价值
数据范围 0<N, VS 100
0<Vi, W; si 100
输入样例
4 5
1 2 3
2 41
3 4 3
452
输出样例:
10
- 分析
代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;
const int mxn = 1e5;
int dp[mxn];
int main()
{
/* fre(); */
int n, m;
sc("%d %d", &n, &m);
int v, w, s;
for_(i, 1, n)
{
sc("%d %d %d", &v, &w, &s);
for_(j, 1, s)
{
rep_(k, m, v)
dp[k] = max(dp[k], dp[k - v] + w);
}
}
pr("%d\n", dp[m]);
return 0;
}
多重背包问题 II
分析
- 题意
有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi, 价值是 Wi
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N, V, 用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 Vi, Wi, Si, 用空格隔开,分别表示第 i 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
ONS 10000<VS20000<Vi, Wi, si 2000
提示:
本题考查多重背包的二进制优化方法。
输入样例
45
123
241
343
45 2
输出样例:
- 分析
- 利用二进制拆分的思想,任何一个数都可,拆分一些 2 的次方数的和(最后一个拆分剩下的数 可能不是 2 某个次方数)
代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;
const int mxn = 1e5;
int dp[mxn];
int n, m;
int v, w, s;
void sov(int t)
{
rep_(j, m, v * t)
dp[j] = max(dp[j], dp[j - v * t] + t * w);
}
int main()
{
/* fre(); */
sc("%d %d", &n, &m);
for_(i, 1, n)
{
sc("%d %d %d", &v, &w, &s);
int t = 1;
while(s >= t)
{
s -= t;
sov(t);
t <<= 1;
}
if(s) sov(s);
}
pr("%d\n", dp[m]);
return 0;
}
多重背包问题 III
分析
- 题意
有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 Vi, 价值是 Wis
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N, V (0 <N< 1000,0 <V< 20000), 用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 Vi, Wi, Si, 用空格隔开,分别表示第 i 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围 0<N<1000
0<V<200000<Vi, Wi, Si S 20000
提示
本题考查多重背包的单调队列优化方法。
输入样例
45
123
241
343
4 5 2
输出样例:
- 分析
- 利用单调队列来维护一个极大值队列
代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;
const int mxn = 1e5;
int dp[2][mxn];
int q[mxn]; //注意这里 我们维护的是一个 极大值的单调队列,
#define cal(x, i) dp[x ^ 1][q[i]] + (k - q[i]) / v * w
int main()
{
/* fre(); */
int n, m;
sc("%d %d", &n, &m);
int v, w, s;
int x = 0; //滚动数组,用的标号
for_(i, 1, n)
{
sc("%d %d %d", &v, &w, &s);
x ^= 1;
for_(j, 0, v - 1) //枚举余数j,j + x * v(就能表示出所有的以j为余数的数), 这样当我们依次枚举了 v的所有余数j之后,就能表示出所有的可能的数(指的是:在(1~s)*v之间的数)了
{
int hh = 0, tt = -1;
for(int k = j; k <= m; k += v) //枚举出来 可能的数
{
while(tt >= hh && (k - q[hh]) / v > s) hh ++; //排除太远,越界的
if(tt >= hh) dp[x][k] = max(dp[x ^ 1][k], cal(x, hh));
while(tt >= hh && dp[x ^ 1][q[hh]] - (q[hh] - j) / v * w <= dp[x ^ 1][k] - (k - j) / v * w) tt --; //消除距离产生的影响
q[++ tt] = k;
}
}
}
pr("%d\n", dp[x][m]);
return 0;
}
混合背包问题
分析
- 题意
有 N 种物品和一个容量是 V 的背包。
物品一共有三类:
・第一类物品只能用 1 次 (01 背包) ;
, 第二类物品可以用无限次 (完全背包) ;
, 第三类物品最多只能用 si 次 (多重背包) ;
每种体积是 Vi, 价值是 Wi
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N,V, 用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 vi, Wi, Si, 用空格隔开,分别表示第 i 种物品的体积、价值和数量。
.Si=-1 表示第 i 种物品只能用 1 次;
.Si=0 表示第 i 种物品可以用无限次.Si>0 表示第 i 种物品可以使用 si 次;
输出格式
输出一个整数,表示最大价值。
数据范围 0<N, VS 1000
0<Vi, Wi S 1000
-1 SSi S 1000
输入样例
输入样例
4 5
1 2 -1
2 4 1
340
4 5 2
输出样例:в - 分析
代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;
const int mxn = 1e5;
int dp[mxn];
int n, m;
int v, w, s;
void sov(int t)
{
rep_(j, m, t * v) dp[j] = max(dp[j], dp[j - t * v] + t * w);
}
int main()
{
/* fre(); */
sc("%d %d", &n, &m);
for_(i, 1, n)
{
sc("%d %d %d", &v, &w, &s);
if(s == 0)
{
for_(j, v, m) dp[j] = max(dp[j], dp[j - v] + w);
}
else
{
if(s == -1) s = 1;
int t = 1;
while(s >= t) //对01背包进行、对多重背包 进行二进制拆分
{
s -= t;
sov(t);
t <<= 1;
}
if(s) sov(s);
}
}
pr("%d\n", dp[m]);
return 0;
}
二维费用的背包问题
分析
- 题意
有 N 件物品和一个容量是 V 的背包,背包能承受的最大量量是 M。
每件物品只能用一次。体积是 Vi, 重量是 mi, 价值是 Wi
求解将哪些物品装入背包,可使物品总体积不超过背包容量,总重量不超过背包可承受的最大重量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N , V,M, 用空格隔开,分别表示物品件数、背包容积和背包可承受的最大重量。
接下来有 N 行,每行三个整数 Vi, mi;, Wi, 用空格隔开,分别表示第 i 件物品的体积、重量和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N S 1000 0<V,M S100 0<Vimi< 100 0<WiS1000
输入样例
456
123
244
345
15 6
输出样例:
- 分析
代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;
const int mxn = 1e3 + 5;
int dp[mxn][mxn];
int N, M, V;
int v, m, w;
int main()
{
/* fre(); */
sc("%d %d %d", &N, &V, &M);
for_(i, 1, N)
{
sc("%d %d %d", &v, &m, &w);
rep_(j, V, v)
{
rep_(k, M, m)
dp[j][k] = max(dp[j][k], dp[j - v][k - m] + w);
}
}
pr("%d\n", dp[V][M]);
return 0;
}
分组背包问题
分析
- 题意
有 N 组物品和一个容量是 V 的背包。
每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 Vj, 价值是 Wi, 其中 i 是组号,j 是组内编号。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行有两个整数 N, V, 用空格隔开,分别表示物品组数和背包容量。
接下来有 N 组数据
・每组数据第一行有一个整数 Si, 表示第 i 个物品组的物品数量;
・每组数据接下来有 Si 行,每行有两个整数 vj, wj, 用空格隔开,分别表示第 i 个物品组的第 j 个物品的体积和价值;
输出格式
输出一个整数,表示最大价值。
数据范围
<N, Vs 1000<Si<100<Vij, Wij S 100
输入样例
35
2
12
: - 分析
代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;
const int mxn = 105;
int n, m;
int dp[mxn];
int v[mxn][mxn], w[mxn][mxn], s[mxn];
int main()
{
/* fre(); */
sc("%d %d", &n, &m);
for_(i, 1, n)
{
sd(s[i]);
for_(j, 1, s[i])
sc("%d %d", &v[i][j], &w[i][j]);
}
for_(i, 1, n)
rep_(j, m, 0)
{
for_(k, 1, s[i])
if(j >= v[i][k]) dp[j] = max(dp[j], dp[j - v[i][k]] + w[i][k]);
}
pr("%d\n", dp[m]);
return 0;
}
有依赖的背包问题
分析
- 题意
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi, 价值是 Wi
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最优选法的方案数。注意答案可能很大,请输出答案模 109+7 的结果。
输入格式
第一行两个整数,N, V, 用空格隔开,分别表示物品数量和背包容积
接下来有 N 行,每行两个整数 Vi, Wi, 用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
输出一个整数,表示方案数模 109+7 的结果
数据范围 0<N, VS 1000
0<Vi, Wi s 1000
输入样例
45
12
2 4
34
4 6
输出样例:2 - 分析
代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 110;
int n,m;
int h[N],e[N],ne[N],idx;
/*h数组是邻接表的头它的下表是当前节点的标号,值是当前结点第一条边的编号(其实是最后加入的那一条边),e数组是边的集合,它的下标是当前边的编号,数值是当前边的终点;
ne是nextedge,如果ne是-1表示当前结点没有下一条边,ne的下标是当前边的编号,数值是当前结点的下一条边的编号,idx用于保存每一条边的上一条边的编号。
这样我们就知道了当前结点的第一条边是几,这个边的终点是那个结点,该节点的下一条边编号是几,那么邻接表就完成了
*/
int v[N],w[N],f[N][N];
void add(int a,int b){
e[idx] = b,ne[idx] = h[a],h[a] = idx++;//该方法同于向有向图中加入一条边,这条边的起点是a,终点是b,加入的这条边编号为idx
}
void dfs(int u){
for(int i = h[u];i!=-1;i = ne[i]){//对当前结点的边进行遍历
int son = e[i];//e数组的值是当前边的终点,即儿子结点
dfs(son);
for(int j = m-v[u];j>=0;j--){
//在做这一题之前,我们首先应该明白,我们正在讨论的 树中的某个节点x为根的整个子树,就相当于一个 分组背包问题,所以树中有多少个节点就是多少个分组背包问题
//遍历背包的容积,因为我们是要遍历其子节点,所以当前节点我们是默认选择的。
//这个时候当前结点我们看成是分组背包中的一个组,"当前正在讨论的子节点son" 的每一种选择我们都看作是组内一种物品(这里的”每一种son的每一种选择“指的是 ”每种选择造成的dp[son][1~m]最优值“);
//从大到小遍历:(举个例子来说为什么这样?,在01背包中的第二从for循环 “从大到小遍历”, 是为了 把dp[][]的二维空间优化成一位空间dp[]; 而在这一题中表面上看:f[][] 是一个二维数组,但是我们要注意到它的第一纬度的“下标”只是起到表示这个子树是第几个分组背包问题)
//我们每一次都默认选择当前结点,因为到最后根节点是必选的。
for(int k = 0;k<=j;k++){//去遍历子节点的组合
f[u][j] = max(f[u][j],f[u][j-k]+f[son][k]);
}
}
}
//加上刚刚默认选择的父节点价值
for(int i = m;i>=v[u];i--){
f[u][i] = f[u][i-v[u]]+w[u];
}
//因为我们是从叶子结点开始往上做,所以如果背包容积不如当前物品的体积大,那就不能选择当前结点及其子节点,因此赋值为零
for(int i = 0;i<v[u];i++){
f[u][i] = 0;
}
}
int main(){
memset(h,-1,sizeof h);
cin>>n>>m;
int root;
for(int i = 1;i<=n;i++){
int p;
cin>>v[i]>>w[i]>>p;
if(p==-1){
root = i;
}else{
add(p,i);//如果不是根节点就加入邻接表,其中p是该节点的父节点,i是当前是第几个节点
}
}
dfs(root);
cout<<f[root][m]<<endl;
return 0;
}
背包问题求最优解方案数
分析
- 题意
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 1 件物品的体积是 Vi, 价值是 Wi.
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最优选法的方案数。注意答案可能很大,请输出答案模 109+7 的结果。
输入格式
第一行两个整数,N, V, 用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 Vi, Wi, 用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
输出一个整数,表示方案数模 109+7 的结果。
数据范围
0<N, Vs 1000
0<Vi, Wi S 1000
输入样例
4 5
1 2
24
34
46
输出样例:
- 分析
代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;
const int mxn = 1005;
int dp[mxn];
int cnt[mxn];
int main()
{
/* fre(); */
int n, m;
int v, w;
sc("%d %d", &n, &m);
for_(i, 0, m) cnt[i] = 1; //注意这里为什么要 cnt 中的元素全部初始化为1,因为最终方案结果不必填满背包
for_(i, 1, n)
{
sc("%d %d", &v, &w);
rep_(j, m, v)
{
int val = dp[j - v] + w;
if(val > dp[j])
{
dp[j] = val;
cnt[j] = cnt[j - v];
}
else if(val == dp[j])
{
cnt[j] += cnt[j - v];
cnt[j] %= mod;
}
}
}
pr("%d\n", cnt[m]);
return 0;
}
背包问题求具体方案 / 记录最优解物品选择的路径
分析
- 题意
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 1 件物品的体积是 vi, 价值是 Wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出字典序最小的方案。这里的字典序是指:所选物品的编号所构成的序列。物品的编号范围是 1… N.
输入格式
第一行两个整数,N, V, 用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 Vi, Wi, 用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
输出一行,包含若干个用空格隔开的整数,表示最优解中所选物品的编号序列,且该编号序列的字典序最小。
物品编号范围是 1… N。
数据范围
0<N, V< 1000
0<Vi, Wi S1000
输入样例 4
输出样例:
- 分析
代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;
const int mxn = 1005;
int dp[mxn][mxn];
int n, m;
int v[mxn], w[mxn];
int main()
{
/* fre(); */
sc("%d %d", &n, &m);
for_(i, 1, n)
sc("%d %d", &v[i], &w[i]);
rep_(i, n, 1)
{
for_(j, 0, m)
{
dp[i][j] = dp[i + 1][j];
if(j >= v[i]) //分类讨论不要忘了!!!
dp[i][j] = max(dp[i][j], dp[i + 1][j - v[i]] + w[i]);
}
}
int sum_v = m;
for_(i, 1, n)
{
//对最后一个物品进行特判
if(i == n && sum_v >= v[i])
{
pr("%d ", i);
break;
}
if(sum_v <= 0) break;
if(sum_v >= v[i] && dp[i][sum_v] == dp[i + 1][sum_v - v[i]] + w[i])
{
sum_v -= v[i];
pr("%d ", i);
}
}
return 0;
}
背包问题第k优解
题目链接
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;
const int mxn = 1010;
int f[mxn][mxn];
int n, m, k;
int v[mxn], w[mxn];
int a[mxn], b[mxn];
int main()
{
/* fre(); */
int T; sd(T);
while(T --)
{
sc("%d %d %d", &n, &m, &k);
for_(i, 1, n) sd(w[i]);
for_(i, 1, n) sd(v[i]);
memset(f, 0, sizeof f);
for_(i, 1, n)
{
rep_(j, m, v[i])
{
for_(l, 1, k) //处理两种情况的状态转移方程分别存储到a、b数组中
{
a[l] = f[j][l];
b[l] = f[j - v[i]][l] + w[i];
}
a[k + 1] = b[k + 1] = -1; //结束标志
int x = 1, y = 1, z = 1;
while((x <= k || y <= k) && z <= k) //注意让求的第k大是不是严格第k大
{
if(a[x] > b[y])
{
f[j][z] = a[x ++];
}
else
{
f[j][z] = b[y ++];
}
if(f[j][z] != f[j][z - 1]) z ++; //非严格第k大 只有不想等 z 才++
}
}
}
pr("%d\n", f[m][k]);
}
return 0;
}