DP背包问题模板

01背包问题

有N件物品和一个容量为V的背包,每件物品只能使用一次
第i件物品的体积是v[i],价值是w[i]。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大,输出最大价值。
数据范围:n,m,v[i],w[i]<=1000

代码如下:

暴力写法(朴素写法)

//01背包模板
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int  w[N], v[N];
int f[N][N];
int n, m;
int main()
{
	cin >> n >> m;   //n件物品 m为背包容量
	for (int i = 1; i <=n; i++) cin >> v[i] >>w[i];   //体积v[i]  价值w[i]
	for (int i = 1; i <=n; i++)
	{                                                //f[i][j]表示:所有只考虑前i个物品 且体积不超过j的总价值
			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]);
		    }
	}
	cout << f[n][m] << endl;    //即为最大价值
}

空间优化后的代码

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N];
int main()
{
	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;
}

完全背包问题

每种物品由1个变为多个
将循环顺序颠倒即可
但是其实这两个问题相差很大。

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N];
int main()
{
	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;
}

多重背包

每种物品的个数不定

化为01背包

//多重背包
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int v[N], w[N], s[N]; //v体积 w价值 s个数
int f[N][N];
int n, m;// n物品种类 m背包容量
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)cin >> v[i] >> w[i] >> s[i];
	for (int i = 1; i <= n; i++)
		for (int j = 0; j <= m; j++)
			for (int k = 0; k * v[i] <= j && k <= s[i]; k++)
				f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]);
	cout << f[n][m] << endl;
	return 0;
}

多重背包优化

//多重背包优化
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int v[N], w[N], s[N]; //v体积 w价值 s个数
int f[N];
int n, m;// n物品种类 m背包容量
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)cin >> v[i] >> w[i] >> s[i];
	for (int i = 1; i <= n; i++)
		for (int j = m; j >=v[i]; j--)
			for (int k = 0; k * v[i] <= j && k <= s[i]; k++)
				f[j] = max(f[j], f[j - k * v[i]] + k * w[i]);
	cout << f[m] << endl;
	return 0;
}

多重背包二进制优化

//多重背包二进制优化
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 20000;
int f[N];
int v[N], w[N];
int main()
{
	int n, m,tot=1;
	cin >> n >> m;
	for (int i = 1; i <= n; i++)    //二进制打包
	{
		int a, b, s;
		cin >> a >> b >> s;
		for (int k = 1; k <= s; k<<=1)
		{
			v[tot] = k * a;
			w[tot++] = k * b;
			s -= k;
		}
		if (s > 0)
		{
			v[tot] = a * s;
			w[tot++] = b * s;
		}
	}
	for (int i = 1; i <= tot; 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;
}

多重背包单调队列优化

//多重背包单调队列优化
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 20100;
int f[N], g[N], q[N];
int main()
{
	int n, m;
	cin >> n >> m;
	for (int i = 0; i < n; i++)
	{
		memcpy(g, f, sizeof(f));
		int v, w, s;
		cin >> v >> w >> s;
		for (int j = 0; j < v; j++)
		{
			int head = 0, tail = -1;
			for (int k = j; k <= m; k += v)
			{
				if (head <= tail && k - s * v > q[head])head++;
				if (head <= tail)f[k] = max(f[k], g[q[head]] + (k - q[head]) / v * w);
				while (head <= tail && g[q[tail]] - (q[tail] - j) / v * w <= g[k] - (k - j) / v * w)tail--;
				q[++tail] = k;
			} 
		}
	}
	cout << f[m] << endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_46750633/article/details/105369208