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;
}