背包问题
- 有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次.
- 第 i 件物品的体积是 vi,价值是 wi.
- 求总体积不超过背包容量情况下的最大价值
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e3 + 10;
int dp[MAXN] = {0};
int main()
{
int n,v;cin >> n >> v;
for(int i = 1;i <= n;i++)
{
int vi,wi;cin >> vi >> wi;
for(int j = v;j >= vi;j--)
dp[j] = max(dp[j],dp[j - vi] + wi);
}
cout << dp[v];
}
- 从后往前遍历就可以实现一次物品的更新
- 从前往回便是无限次物品的更新
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e3 + 10;
int dp[MAXN] = {0};
int main()
{
int n,v;cin >> n >> v;
for(int i = 1;i <= n;i++)
{
int vi,wi;cin >> vi >> wi;
for(int j = 0;j <= v - vi;j++)
dp[j + vi] = max(dp[j + vi],dp[j] + wi);
}
cout << dp[v];
}
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e2 + 10;
int dp[MAXN] = {0};
int main()
{
int n,v;cin >> n >> v;
for(int i = 1;i <= n;i++)
{
int vi,wi,si;cin >> vi >> wi >> si;
for(int k = 1;k <= si;k++)
for(int j = v;j >= vi;j--)
dp[j] = max(dp[j],dp[j - vi] + wi);
}
cout << dp[v];
}
- 从后往前为一次一次,那么只要来s次从后往前遍历即可(在数据量较小的情况下)
- 同上,当数据量较大时
- 使用二进制优化
- 如10010110b可以分解为01111111b + 00010111b,而如果将二进制的1表示一次计算,那么这个计算的值的范围便是[0~10010110b]
- 从后往前遍历10010110b如果si&1 == 1就执行两次遍历,否者执行一次,然后si>>=1,直到si < 2
#include<bits/stdc++.h>
using namespace std;
const int MAXV = 2e3 + 10;
int dp[MAXV] = {0};
int main()
{
int n,v;cin >> n >> v;
for(int i = 1;i <= n;i++)
{
int vi,wi,si;cin >> vi >> wi >> si;
int ptop = 0;
while(si >= 2)
{
if(si&1)
for(int j = v;j >= vi*(1 << ptop);j--)
dp[j] = max(dp[j],dp[j - vi * (1 << ptop)] + (1 << ptop) * wi);
for(int j = v;j >= vi*(1 << ptop);j--)
dp[j] = max(dp[j],dp[j - vi * (1 << ptop)] + (1 << ptop) * wi);
ptop++;
si >>= 1;
}
for(int j = v;j >= vi;j--)
dp[j] = max(dp[j],dp[j - vi] + wi);
}
cout << dp[v];
}
#include<bits/stdc++.h>
using namespace std;
const int MAXV = 2e4 + 10;
int dp[2][MAXV] = {0};
int deq[MAXV];//单调队列
int main()
{
ios::sync_with_stdio(false);//写了using namespace std;
int n,v;cin >> n >> v;
int t = 0;
for(int i = 1;i <= n;i++)
{
int vi,wi,si;cin >> vi >> wi >> si;
for(int j = 0;j < vi;j++)//枚举余数
{
int ptop = 0,pend = 0;//队头,队尾
deq[0] = 0;
for(int k = 1;v >= k * vi + j;k++)
{
while(pend >= ptop && k - deq[ptop] > si)
ptop++;
int tem = dp[1 - t][k * vi + j];
if(pend >= ptop)
dp[t][k * vi + j] = max(tem,dp[1 - t][deq[ptop] * vi + j] + wi * (k - deq[ptop]));
while(pend >= ptop && tem >= dp[1 - t][deq[pend] * vi + j] + wi * (k - deq[pend]))
pend--;
deq[++pend] = k;
}
}
t = 1 - t;
}
cout << dp[1 - t][v];
}
#include<bits/stdc++.h>
using namespace std;
const int MAXV = 101;
const int MAXM = 101;
int dp[MAXM][MAXV] = {0};
int main()
{
int n,v,m;cin >> n >> v >> m;
for(int i = 1;i <= n;i++)
{
int vi,mi,wi;cin >> vi >> mi >> wi;
for(int j = v;j >= vi;j--)
for(int k = m;k >= mi;k--)
dp[k][j] = max(dp[k][j],dp[k - mi][j - vi] + wi);
}
cout << dp[m][v];
}
- 一组里只能拿一个
- 首先只能拿一个,所以从后往前遍历
- 一组内的物品不相互影响,所以用两个数组代表一组前和一组后的价值
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 101;
int dp[2][MAXN] = {0};
int main()
{
int n,v,t = 1;cin >> n >> v;
while(n--)
{
int si;cin >> si;
for(int i = 1;i <= si;i++)
{
int vi,wi;cin >> vi >> wi;
for(int j = v;j >= vi;j--)
dp[t][j] = max(dp[t][j],dp[1 - t][j - vi] + wi);
}
for(int i = 1;i <= v;i++)
dp[1 - t][i] = dp[t][i];
}
cout << dp[t][v];
}