背包DP
背包DP
01背包 (每个物品只能选一个或者不选)
//#pragma comment(linker, "/STACK:10240000000000,10240000000000")
//#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for (int i=(a);i<=(b);++i)
#define per(i,b,a) for (int i=(b);i>=(a);--i)
#define lb lower_bound
#define ub upper_bound
#define pb push_back
#define itt iterator
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0);
#define lowbit(x) x & (-x)
#define clr(x) memset(x, 0, sizeof(x));
#define fi first
#define se second
#define mp make_pair
#define MOD 1000000007
typedef vector<int> vii;
typedef vector<long long> vll;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef set<int> si;
typedef set<ll> sll;
const pii movee[] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
ll ksm(ll a, ll b, ll p) {if (b == 0) return 1; ll ns = ksm(a, b >> 1, p); ns = ns * ns % p; if (b & 1) ns = ns * a % p; return ns;}
const int MAXN = 0x7fffffff;
const int N = 1010;
int n, m;
int f[N];
int w[N], v[N];
int main ()
{
//IOS;
cin >> n >> m;
for(int i = 1; i <= n; i ++) cin >> v[i] >> w[i];
for(int i = 1; i <= n; i ++)
for(int j = m; j >= v[i]; j --)
f[j] = max(f[j], f[j - v[i]] + w[i]);
cout << f[m] << endl;
return 0;
}
/*
*/
完全背包 (每个物品可以选无限多个)
//#pragma comment(linker, "/STACK:10240000000000,10240000000000")
//#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for (int i=(a);i<=(b);++i)
#define per(i,b,a) for (int i=(b);i>=(a);--i)
#define lb lower_bound
#define ub upper_bound
#define pb push_back
#define itt iterator
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0);
#define lowbit(x) x & (-x)
#define clr(x) memset(x, 0, sizeof(x));
#define fi first
#define se second
#define mp make_pair
#define MOD 1000000007
typedef vector<int> vii;
typedef vector<long long> vll;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef set<int> si;
typedef set<ll> sll;
const pii movee[] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
ll ksm(ll a, ll b, ll p) {if (b == 0) return 1; ll ns = ksm(a, b >> 1, p); ns = ns * ns % p; if (b & 1) ns = ns * a % p; return ns;}
const int MAXN = 0x7fffffff;
const int N = 1010;
int v[N], w[N], f[N];
int n, m;
int main ()
{
//IOS;
cin >> n >> m;
for(int i = 1; i <= n; i ++) cin >> v[i] >> w[i];
for(int i = 1; i <= n; i ++)
for(int j = v[i]; j <= m; j ++)
f[j] = max(f[j], f[j - v[i]] + w[i]);
cout << f[m] << endl;
return 0;
}
/*
*/
多重背包+二进制优化 (每个物品最多选k个)
//#pragma comment(linker, "/STACK:10240000000000,10240000000000")
//#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for (int i=(a);i<=(b);++i)
#define per(i,b,a) for (int i=(b);i>=(a);--i)
#define lb lower_bound
#define ub upper_bound
#define pb push_back
#define itt iterator
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0);
#define lowbit(x) x & (-x)
#define clr(x) memset(x, 0, sizeof(x));
#define fi first
#define se second
#define mp make_pair
#define MOD 1000000007
typedef vector<int> vii;
typedef vector<long long> vll;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef set<int> si;
typedef set<ll> sll;
const pii movee[] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
ll ksm(ll a, ll b, ll p) {if (b == 0) return 1; ll ns = ksm(a, b >> 1, p); ns = ns * ns % p; if (b & 1) ns = ns * a % p; return ns;}
const int MAXN = 0x7fffffff;
const int N = 25000, M = 2010;
int v[N], w[N], f[M];
int n, m;
int main ()
{
//IOS;
cin >> n >> m;
int cnt = 0;
// 将s件物品拆成2^k打包在一起的组合(k = 0, 1, 2....)
for(int i = 1; i <= n ; i ++)
{
int a, b, s;
cin >> a >> b >> s;
int k = 1;
while(k <= s)
{
v[++cnt] = a * k;
w[cnt] = b * k;
s -= k;
k <<= 1;
}
//如果用2的k次方表示后还有剩余,就再打一个包
if(s) v[++cnt] = s * a, w[cnt] = s * b;
}
// 01板子
for(int i = 1; i <= cnt; i ++)
for(int j = m; j >= v[i]; j --)
f[j] = max(f[j], f[j - v[i]] + w[i]);
cout << f[m] << endl;
return 0;
}
/*
*/
分组背包(每组物品最多选一个)
//#pragma comment(linker, "/STACK:10240000000000,10240000000000")
//#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for (int i=(a);i<=(b);++i)
#define per(i,b,a) for (int i=(b);i>=(a);--i)
#define lb lower_bound
#define ub upper_bound
#define pb push_back
#define itt iterator
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0);
#define lowbit(x) x & (-x)
#define clr(x) memset(x, 0, sizeof(x));
#define fi first
#define se second
#define mp make_pair
#define MOD 1000000007
typedef vector<int> vii;
typedef vector<long long> vll;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef set<int> si;
typedef set<ll> sll;
const pii movee[] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
ll ksm(ll a, ll b, ll p) {if (b == 0) return 1; ll ns = ksm(a, b >> 1, p); ns = ns * ns % p; if (b & 1) ns = ns * a % p; return ns;}
const int MAXN = 0x7fffffff;
const int N = 110;
int v[N][N], w[N][N];
int n, m;
int f[N], s[N];
int main ()
{
//IOS;
cin >> n >> m;
for(int i = 1; i <= n; i ++)
{
cin >> s[i];
for(int j = 1; j <= s[i]; j ++)
cin >> v[i][j] >> w[i][j];
}
for(int i = 1; i <= n; i ++)
for(int j = m; j >= 0; j --)
for(int k = 1; k <= s[i]; k ++)
if(v[i][k] <= j)
f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);
cout << f[m] << endl;
return 0;
}
/*
*/
背包DP 求具体方案
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int f[N][N];
int v[N], w[N];
int n, m;
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i ++) cin >> v[i] >> w[i];
for(int i = n; i >= 1; i --)
{
for(int j = 0; j <= m; j ++)
{
f[i][j] = f[i + 1][j];
if(j >= v[i]) f[i][j] = max(f[i][j], f[i + 1][j - v[i]] + w[i]);
}
}
int j = m;
for(int i = 1; i <= n; i ++)
{
if(f[i][j] == f[i + 1][j - v[i]] + w[i] && j >= v[i])
{
cout << i << " ";
j -= v[i];
}
}
return 0;
}
一般做01背包,我们都是从第一个物品往第n个物品推,但因为要求最小字典序,所以我们要从第n个往第1个进行DP的状态转移
DP的答案是f[1] [m],而求方案数则是再从第1个物品回推回去,如果f[i] [j] 是从 f[i + 1] [j - v[i]] + w[i]转移而来,则说明第i个物品被选,就输出i即可。
其他背包问题求具体方案思路一致
树形背包
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
//f[i][j]表示选取i结点,且体积不大于j的最大价值
int f[N][N];
int v[N], w[N];
vector <int> g[N];
int n, m;
void dfs(int u)
{
for (int j = v[u]; j <= m; j ++) f[u][j] = w[u]; //选取父节点
for(auto &son : g[u])
{
dfs(son);
for(int j = m; j >= v[u]; j --) //整棵子树分配的空间
for(int k = 0; k <= j - v[u]; k ++)//给孩子分配的不同空间,分配的空间要预留一部分给父节点
f[u][j] = max(f[u][j], f[u][j - k] + f[son][k]);
}
}
int main()
{
cin >> n >> m;
int root;
for (int i = 1; i <= n; i ++)
{
int a, b, p;
cin >> a >> b >> p;
v[i] = a, w[i] = b;
if(p == -1) root = i;
else g[p].push_back(i);
}
dfs(root); //从根开始遍历
cout << f[root][m] << endl;
return 0;
}
从父节点往子树划分集合的方式是用体积来划分,因为如果按照方案数来如果有100棵子树就有\(2^{100}\)种状态,太大不可取,所以改用体积。对于每个父节点,其实内部是一个分组背包问题。

浙公网安备 33010602011771号