因为⼀个组⾥⾯最多只能挑⼀个元素,所以我们就以⼀个组为单位。
dp[i][j]
表⽰从前i 组中挑选物品,总重量不超过j 的情况下,最⼤的价值。dp[n][m]
就是最终结果。dp[i - 1][j - a] + b
。dp[i][j]
。#include
using namespace std;
typedef pair PII;
const int N = 1010;
int n, m, cnt;
vector g[N];
int f[N][N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> m >> n;
for (int i = 1; i <= n; i++)
{
int a, b, c; cin >> a >> b >> c;
cnt = max(c, cnt);
g[c].push_back({a, b});
}
//动态规划
for (int i = 1; i <= cnt; i++)
{
for (int j = m; j >= 0; j--)
{
f[i][j] = f[i-1][j];
//在这一组选
for (auto& t : g[i])
{
int a = t.first, b = t.second;
if (j >= a) f[i][j] = max(f[i][j], f[i-1][j-a] + b);
}
}
}
cout << f[cnt][m] << endl;
return 0;
}
空间优化:
#include
using namespace std;
typedef pair PII;
const int N = 1010;
int n, m, cnt;
vector g[N];
int f[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> m >> n;
for (int i = 1; i <= n; i++)
{
int a, b, c; cin >> a >> b >> c;
cnt = max(c, cnt);
g[c].push_back({a, b});
}
//动态规划
for (int i = 1; i <= cnt; i++)
{
for (int j = m; j >= 0; j--)
{
//在这一组选
for (auto& t : g[i])
{
int a = t.first, b = t.second;
if (j >= a) f[j] = max(f[j], f[j-a] + b);
}
}
}
cout << f[m] << endl;
return 0;
}
⼀个城堡⼀个城堡分析,对于第n个城堡,考虑派遣的⼈数应该在所有玩家对这个城堡派遣⼈数中考虑。⽐如⽰例⼆的第三个城堡,我们考虑派遣的⼈数就是1和13 (因为要严格⼤于两倍,⼤⼀点就是最好的)。
因此,把每⼀个城堡看成⼀个⼩组,所有玩家在这个城堡派遣的⼈数看成⼀个⼀个物品,要求的就是在派遣⼈数不超过m 的情况下的最⼤得分,符合分组背包。
⼩优化:对每个城堡中玩家的派遣⼈数从⼩到⼤排序,这样在选择第k个⼈数的时候,总得分就是k × i。
dp[i][j]
表⽰:分配前i 个城堡,在总⼈数不超过j 的情况下,最⼤的得分。dp[n][m]
就是最终结果。a[i][k]
,此时的最⼤得分为dp[i - 1][j - a[i][k]] + i × k
。#include
using namespace std;
const int N = 110, M = 2e4 + 10;
int s, n, m;
int a[N][N];
int f[N][M];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> s >> n >> m;
for (int i = 1; i <= s; i++)
{
for (int j = 1; j <= n; j++)
{
cin >> a[j][i];
a[j][i] = a[j][i] * 2 + 1;
}
}
//每一行排序
for (int i = 1; i <= n; i++)
{
sort(a[i]+1, a[i]+1+s);
}
//分组背包
for (int i = 1; i <= n; i++)
{
for (int j = 0; j <= m; j++)
{
f[i][j] = f[i-1][j];
for (int k = 1; k <= s && a[i][k] <= j; k++)
{
f[i][j] = max(f[i][j], f[i-1][j-a[i][k]] + k*i);
}
}
}
cout << f[n][m] << endl;
return 0;
}
分类讨论
#include
using namespace std;
const int N = 1e4 + 10, M = 1010;
int n, m;
int t[N], c[N], p[N];
int f[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t1, t2, t3, t4; char ch;
cin >> t1 >> ch >> t2 >> t3 >> ch >> t4 >> n;
m = t3 * 60 + t4 - (t1 * 60 + t2);
for (int i = 1; i <= n; i++) cin >> t[i] >> c[i] >> p[i];
for (int i = 1; i <= n; i++)
{
if (p[i] == 0) //完全背包
{
for (int j = t[i]; j <= m; j++)
{
f[j] = max(f[j], f[j-t[i]] + c[i]);
}
}
else if (p[i] == 1) //01背包
{
for (int j = m; j >= t[i]; j--)
{
f[j] = max(f[j], f[j-t[i]] + c[i]);
}
}
else //多重背包
{
for (int j = m; j >= t[i]; j--)
{
for (int k = 1; k <= p[i] && k*t[i] <= j; k++)
{
f[j] = max(f[j], f[j-t[i]*k] + c[i]*k);
}
}
}
}
cout << f[m] << endl;
return 0;
}
多重背包和01背包合并
#include
using namespace std;
const int N = 1e4 + 10, M = 1010;
int n, m;
int t[N], c[N], p[N];
int f[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t1, t2, t3, t4; char ch;
cin >> t1 >> ch >> t2 >> t3 >> ch >> t4 >> n;
m = t3 * 60 + t4 - (t1 * 60 + t2);
for (int i = 1; i <= n; i++) cin >> t[i] >> c[i] >> p[i];
for (int i = 1; i <= n; i++)
{
if (p[i] == 0) //完全背包
{
for (int j = t[i]; j <= m; j++)
{
f[j] = max(f[j], f[j-t[i]] + c[i]);
}
}
else //多重背包或者01背包
{
for (int j = m; j >= t[i]; j--)
{
for (int k = 1; k <= p[i] && k*t[i] <= j; k++)
{
f[j] = max(f[j], f[j-t[i]*k] + c[i]*k);
}
}
}
}
cout << f[m] << endl;
return 0;
}
⽆⾮就是在01背包的基础上多加了⼀维,那我们就把状态表⽰也加上⼀维即可。
dp[i][j][k]
表⽰:从前i个⼈中挑选,伪装能⼒之和不超过j,总⼯资不超过x,此时能获取到的最多资料总数。dp[n][m][x]
就是结果dp[i - 1][j][k]
;j-b[i]
,总⼯资不超过k-c[i]
时的最多⼯资,再加上第i个⼈的⼯资。也就是dp[i - 1][j - b[i]][k - c[i]] + a[i]
。#include
using namespace std;
const int N = 110, M = 1010;
int n, m, x;
int a[N], b[N], c[N];
int f[M][M];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m >> x;
for (int i = 1; i <= n; i++) cin >> a[i] >> b[i] >> c[i];
for (int i = 1; i <= n; i++)
for (int j = m; j >= b[i]; j--)
for (int k = x; k >= c[i]; k--)
{
f[j][k] = max(f[j][k], f[j-b[i]][k-c[i]] + a[i]);
}
cout << f[m][x] << endl;
return 0;
}