背包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 求具体方案

12. 背包问题求具体方案 - AcWing题库

#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}\)种状态,太大不可取,所以改用体积。对于每个父节点,其实内部是一个分组背包问题。

posted @ 2021-11-09 21:19  Yra  阅读(41)  评论(0)    收藏  举报