01背包问题的子集树搜索
如题:
经典01背包问题,直接代码反映心路历程。
//
// Created by _thinkPad on 2023/10/16.
//
#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
using namespace std;
/*
* 第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
* 接下来有 N行,每行两个整数 vi,wi,用空格隔开
* 分别表示第 i 件物品的体积和价值。
*/
// 尝试一下bfs,十分滴粗糙
void s0() {
int N, V;
cin >> N >> V;
vector<int> v, w;
for (int i = 0; i < N; i++) {
int t1, t2;
cin >> t1 >> t2;
v.push_back(t1);
w.push_back(t2);
}
int i = 0;
queue<int> qt;
queue<pair<int, int>> qvw;
qt.push(i);
qvw.emplace(V, 0);
int res = 0;
while (!qt.empty()) {
i = qt.front();
pair<int, int> tvw = qvw.front();
qvw.pop();
qt.pop();
if (tvw.second >= res)
res = tvw.second;
if (i == N)
continue;
if (tvw.first >= v[i]) {
qt.push(i + 1);
qvw.emplace(tvw.first - v[i], tvw.second + w[i]);
}
qt.push(i + 1);
qvw.emplace(tvw.first, tvw.second);
}
cout << res << endl;
}
// 暴力bfs超时,进行剪枝优化一下,使用一个sum对未来最大进行预判
void s1() {
int N, V;
cin >> N >> V;
vector<int> v, w, sum;
for (int i = 0; i < N; i++) {
int t1, t2;
cin >> t1 >> t2;
v.push_back(t1);
w.push_back(t2);
sum.push_back(t2);
}
for (int i = N - 2; i >= 0; i--) {
sum[i] += sum[i + 1];
}
int i = 0, vi = V, wi = 0, sumi = sum[0];
queue<int> qi, qv, qw, qsum;
qi.push(i);
qv.push(vi);
qw.push(wi);
qsum.push(sumi);
int res = 0;
while (!qi.empty()) {
i = qi.front(), vi = qv.front(), wi = qw.front(), sumi = qsum.front();
qi.pop(), qv.pop(), qw.pop(), qsum.pop();
if (wi >= res)
res = wi;
if (res >= sumi + wi)
continue;
if (i == N)
continue;
if (vi >= v[i]) {
qi.push(i + 1);
qv.push(vi - v[i]);
qw.push(wi + w[i]);
qsum.push(sum[i + 1]);
}
qi.push(i + 1);
qv.push(vi);
qw.push(wi);
qsum.push(sum[i + 1]);
}
cout << res << endl;
}
// 使用限界函数还是失败了(优化了200ms),水一个dfs(就是把queue换stack哈哈哈哈哈)
void s2() {
int N, V;
cin >> N >> V;
vector<int> v, w, sum;
for (int i = 0; i < N; i++) {
int t1, t2;
cin >> t1 >> t2;
v.push_back(t1);
w.push_back(t2);
sum.push_back(t2);
}
for (int i = N - 2; i >= 0; i--) {
sum[i] += sum[i + 1];
}
int i = 0, vi = V, wi = 0, sumi = sum[0];
stack<int> qi, qv, qw, qsum;
qi.push(i);
qv.push(vi);
qw.push(wi);
qsum.push(sumi);
int res = 0;
while (!qi.empty()) {
i = qi.top(), vi = qv.top(), wi = qw.top(), sumi = qsum.top();
qi.pop(), qv.pop(), qw.pop(), qsum.pop();
if (wi >= res)
res = wi;
if (res >= sumi + wi)
continue;
if (i == N)
continue;
if (vi >= v[i]) {
qi.push(i + 1);
qv.push(vi - v[i]);
qw.push(wi + w[i]);
qsum.push(sum[i + 1]);
}
qi.push(i + 1);
qv.push(vi);
qw.push(wi);
qsum.push(sum[i + 1]);
}
cout << res << endl;
}
// dfs很显然也是不行的,本质上和bfs差不多,整一个优先队列试试
// 就是按照可以分割物品进行横刀阔斧的剪枝
// 不过优先队列剪枝过程太麻烦了,还需要排序
// 为了方便定义一个类记录状态和排序
class bestQu {
public:
int v, w;
float preW;
explicit bestQu(int vv = 0, int ww = 0) {
v = vv, w = ww;
preW = (float) ww / vv;
}
bool operator>(bestQu c) const {
return (double(w) / v) > (double(c.w) / c.v);
}
bool operator<(bestQu c) const {
return (double(w) / v) < (double(c.w) / c.v);
}
bool operator==(bestQu c) const {
return (w == c.w && v == c.v);
}
void print() {
cout << "体积: " << v << " 价值: " << w << " 单位体积价值: " << preW << endl;
}
static inline void getBestP(int &bestp, const vector<bestQu> &VW, int V, int i) {
bestp = 0;
for (int j = i; j < VW.size(); j++) {
if (V > 0) {
if (V >= VW[i].v) {
V -= VW[i].v;
bestp += VW[i].w;
} else {
// bestp += (int) VW[i].preW * V;
bestp += (int) (VW[i].preW * V);// 这里这个(int)强制转换一定要把后面括起来,要不然边界会出问题
V = 0;
}
} else {
break;
}
}
}
};
void s3() {
int N, V;
cin >> N >> V;
vector<bestQu> VW;
int bestp;
for (int i = 0; i < N; i++) {
int t1, t2;
cin >> t1 >> t2;
VW.emplace_back(t1, t2);
}
sort(VW.rbegin(), VW.rend());
bestQu::getBestP(bestp, VW, V, 0);
int i = 0, vi = V, wi = 0;
queue<int> qi, qv, qw;
qi.push(i);
qv.push(vi);
qw.push(wi);
int res = 0;
while (!qi.empty()) {
i = qi.front(), vi = qv.front(), wi = qw.front();
bestQu::getBestP(bestp, VW, vi, i);
qi.pop(), qv.pop(), qw.pop();
if (wi >= res)
res = wi;
if (res >= bestp + wi)
continue;
if (i == N)
continue;
if (vi >= VW[i].v) {
qi.push(i + 1);
qv.push(vi - VW[i].v);
qw.push(wi + VW[i].w);
}
qi.push(i + 1);
qv.push(vi);
qw.push(wi);
}
cout << res << endl;
}
// 经过不断的改良,搜索法过了
// 那么再试试动态规划吧
void s4() {
int N, V;
cin >> N >> V;
vector<int> v, w;
for (int i = 0; i < N; i++) {
int t1, t2;
cin >> t1 >> t2;
v.push_back(t1);
w.push_back(t2);
}
// 构建N*V的矩阵
vector<vector<int>> mx(N, vector<int>(V + 1, 0));
// 初始化第一行
for (int i = 0; i <= V; i++) {
if (i < v[0])
continue;
mx[0][i] = w[0];
}
// f[i,j] = Max{ f[i-1,j-Wi]+Pi( j >= Wi ), f[i-1,j] }
for (int i = 1; i < N; i++) {
for (int j = 1; j <= V; j++) {
if (j < v[i])
mx[i][j] = mx[i - 1][j];
else
mx[i][j] = max(mx[i - 1][j], mx[i - 1][j - v[i]] + w[i]);
}
}
cout << mx[N - 1][V] << endl;
}
// 动态规划求也是相当的快,继续改一改,
// 基本上就优化到这里了,和使用优先队列几乎时间复杂度一致
void s5() {
int N, V;
cin >> N >> V;
vector<int> v(N, 0), w(N, 0);
for (int i = 0; i < N; i++) {
int t1, t2;
cin >> t1 >> t2;
v[i] = t1, w[i] = t2;
}
// 构建N*V(V+1)的矩阵
vector<vector<int>> mx(N, vector<int>(V + 1, 0));
// 初始化第一行
for (int i = 0; i <= V; i++) {
if (i < v[0])
continue;
mx[0][i] = w[0];
}
for (int i = 1; i < N; i++) {
for (int j = 1; j <= V; j++) {
if (j < v[i])
mx[i][j] = mx[i - 1][j];
else
mx[i][j] = max(mx[i - 1][j], mx[i - 1][j - v[i]] + w[i]);
}
}
cout << mx[N - 1][V] << endl;
}
#ifndef test
int main() {
s4();
return 0;
}
#endif
前三个一样的复杂度
优先队列和动态规划虽然思路不同,但是测试时间基本上一样的。