20260722 - 背包
A - Fair Division
罚时总结:想当然了
显然,设 \(dp(j)\) 表示 \(j\) 能不能到,转移和背包一样了。
#include <bits/stdc++.h>
using namespace std;
#define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 30000 + 7;
constexpr int P = 998244353;
int m, n;
int v[N], w[N], dp[N];
int main() {
scanf("%d%d", &m, &n);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &v[i], &w[i]);
}
for (int i = 1; i <= n; i++) {
for (int j = m; j >= v[i]; j--) {
dp[j] = max(dp[j], dp[j - v[i]] + v[i] * w[i]);
}
}
printf("%d\n", dp[m]);
return 0;
}
B - 开心的金明
显然是裸的 DP,和采药几乎一模一样。
#include <bits/stdc++.h>
using namespace std;
#define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 30000 + 7;
constexpr int P = 998244353;
int m, n;
int v[N], w[N], dp[N];
int main() {
scanf("%d%d", &m, &n);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &v[i], &w[i]);
}
for (int i = 1; i <= n; i++) {
for (int j = m; j >= v[i]; j--) {
dp[j] = max(dp[j], dp[j - v[i]] + v[i] * w[i]);
}
}
printf("%d\n", dp[m]);
return 0;
}
C - 总分 Score Inflation
显然,也是裸的 DP。
#include <bits/stdc++.h>
using namespace std;
#define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 1e4 + 7;
constexpr int P = 998244353;
int m, n;
int v[N], w[N];
ll dp[N];
int main() {
scanf("%d%d", &m, &n);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &w[i], &v[i]);
}
for (int i = 1; i <= n; i++) {
for (int j = v[i]; j <= m; j++) {
dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
}
}
printf("%lld\n", dp[m]);
return 0;
}
D - Money in Hand
显然,也是裸的 DP。
#include <bits/stdc++.h>
using namespace std;
#define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 30000 + 7;
constexpr int P = 998244353;
int m, n;
int v[N], w[N], dp[N];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &v[i], &w[i]);
}
dp[0] = 1;
for (int i = 1; i <= n; i++) {
for (int l = w[i]; l >= 1; l--) {
for (int j = m; j >= v[i]; j--) {
dp[j] |= dp[j - v[i]];
}
}
}
printf("%s\n", dp[m] ? "Yes" : "No");
return 0;
}
E - 榨取kkksc03
显然,也是裸的 DP。
#include <bits/stdc++.h>
using namespace std;
#define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 200 + 7;
constexpr int P = 998244353;
int n, m, t;
int v[N], w[N], T[N];
ll dp[N][N];
int main() {
scanf("%d%d%d", &n, &m, &t);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &w[i], &T[i]);
}
for (int i = 1; i <= n; i++) {
for (int j = m; j >= w[i]; j--) {
for (int k = t; k >= T[i]; k--) {
dp[j][k] = max(dp[j][k], dp[j - w[i]][k - T[i]] + 1);
}
}
}
printf("%lld\n", dp[m][t]);
return 0;
}
F - Cutting
这题是背包吗?
显然,如果某一个位置合法,那么就可以加入到一个堆中,显然每次选最小的一定最优。
#include <bits/stdc++.h>
using namespace std;
#define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 100 + 7;
constexpr int P = 998244353;
int n, m, a[N], pre[N];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
pre[i] = pre[i - 1] + ((a[i] & 1) ? 1 : -1);
}
priority_queue<int, vector<int>, greater<int>> q;
for (int i = 1; i < n; i++) if (!pre[i]) q.push(abs(a[i + 1] - a[i]));
int cnt = 0;
while (!q.empty()) {
int u = q.top();
q.pop();
if (u > m) break;
m -= u;
++cnt;
}
printf("%d\n", cnt);
return 0;
}
G - Robot Arms 2
显然,可以有两个方向,判断奇偶性,分别搞。
建议不要用 map<int, map<int, bool>>。
#include <bits/stdc++.h>
using namespace std;
// #define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 1e3 + 7, K = 5e4 + 15;
constexpr int P = 998244353;
int n, x, y, a[N], b[N], c[N];
map<int, map<int, bool>> f, g;
int main() {
scanf("%d%d%d", &n, &x, &y);
int tot1 = 0, tot2 = 0;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
if (i & 1) b[++tot1] = a[i];
else c[++tot2] = a[i];
}
f[1][a[1]] = 1;
for (int i = 2; i <= tot1; i++) {
const auto &pre = f[i - 1];
for (const auto &[fi, se] : pre) {
f[i][fi - b[i]] = f[i][fi + b[i]] = true;
}
}
g[0][0] = 1;
for (int i = 1; i <= tot2; i++) {
const auto &pre = g[i - 1];
for (const auto &[fi, se] : pre) {
g[i][fi - c[i]] = g[i][fi + c[i]] = true;
}
}
debug(f[tot1][x]);
if (f[tot1][x] && g[tot2][y]) puts("Yes");
else puts("No");
return 0;
}
H - Buns
显然,这就是一个裸的 DP。
#include <bits/stdc++.h>
using namespace std;
#define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 10000 + 7;
constexpr int P = 998244353;
int n, m, c, d, dp[N];
int main() {
scanf("%d%d%d%d", &n, &m, &c, &d);
for (int i = 1; i <= n; i++) {
dp[i] = i / c * d;
}
for (int i = 1; i <= m; i++) {
int a, b, c, d;
scanf("%d%d%d%d", &a, &b, &c, &d);
for (int j = 1; j <= a / b; j++)
for (int k = n; k >= c; k--)
dp[k] = max(dp[k], dp[k - c] + d);
}
printf("%d\n", dp[n]);
return 0;
}
I - 金明的预算方案
显然,就是一个分组背包,分枚举选了几个。
推广到多个的话,如果没有环的话,就可以直接树上背包,否则据说是一道 NP 难度的问题。
#include <bits/stdc++.h>
using namespace std;
// #define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 3.2 * 1e4 + 7;
constexpr int P = 998244353;
int m, n;
int v[N], p[N], q[N], val[N], vc[N][10], tot[N];
vector<array<int, 2>> ve;
int dp[2][N];
int main() {
scanf("%d%d", &m, &n);
for (int i = 1; i <= n; i++) {
scanf("%d%d%d", &v[i], &p[i], &q[i]);
val[i] = v[i] * p[i];
if (q[i] == 0) {
} else {
vc[q[i]][++tot[q[i]]] = i;
}
debug(tot[q[i]]);
}
int *f = dp[0], *g = dp[1];
for (int i = 1; i <= n; i++) {
if (!q[i]) {
ve.clear();
ve.push_back({v[i], val[i]});
if (vc[i][1]) ve.push_back({v[vc[i][1]] + v[i], val[vc[i][1]] + val[i]});
debug(vc[i][1]);
if (vc[i][2]) ve.push_back({v[vc[i][2]] + v[i], val[vc[i][2]] + val[i]});
debug(vc[i][2]);
if (vc[i][1] && vc[i][2]) ve.push_back({v[vc[i][1]] + v[i] + v[vc[i][2]], val[vc[i][2]] + val[i] + val[vc[i][1]]});
for (int j = 0; j <= m; j++) {
g[j] = f[j];
for (auto [nv, nw] : ve) {
if (j >= nv) {
g[j] = max(g[j], f[j - nv] + nw);
}
}
}
swap(f, g);
}
}
for (int i = 1; i <= n; i++) debug(f[i]);
printf("%d\n", f[m]);
return 0;
}
J - 货币系统
显然,如果出现了倍数的情况肯定得删除,然后就没了。
#include <bits/stdc++.h>
using namespace std;
#define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 100 + 7;
constexpr int P = 998244353;
int n, a[N];
void solve() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
sort(a + 1, a + n + 1);
vector<int> dp(a[n] + 1);
int ans = n;
dp[0] = 1;
for (int i = 1; i <= n; i++) {
if (dp[a[i]]) --ans;
for (int j = a[i]; j <= a[n]; j++) {
dp[j] |= dp[j - a[i]];
}
}
printf("%d\n", ans);
}
int main() {
int oT_To = 1;
scanf("%d", &oT_To);
while (oT_To--) solve();
return 0;
}
K Birds
显然,\(W, B, X\) 是常数和 \(\sum c_i \le 10^4\) 是很有用的。
那么设 \(dp(i, j)\) 表示前 \(i\) 个,搞了 \(j\) 只鸟的最大剩余魔力。
然后就是普通的背包了。
#include <bits/stdc++.h>
using namespace std;
#define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define int ll
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 1e4 + 7, M = 1e3 + 7;
constexpr int P = 998244353;
int n, w, b, x, c[N], cost[N];
ll dp[M][N];
signed main() {
scanf("%lld%lld%lld%lld", &n, &w, &b, &x);
for (int i = 1; i <= n; i++)
scanf("%lld", &c[i]);
for (int i = 1; i <= n; i++)
scanf("%lld", &cost[i]);
memset(dp, -1, sizeof(dp));
dp[0][0] = w;
int maxbird = 0;
for (int i = 1; i <= n; i++) {
maxbird += c[i];
for (int j = 0; j <= maxbird; j++) {
for (int k = 0; k <= c[i] && k <= j; k++) {
if (dp[i - 1][j - k] == -1) continue;
if (dp[i - 1][j - k] - (ll)cost[i] * k < 0) continue;
dp[i][j] = max(dp[i][j], min<ll>(dp[i - 1][j - k] - (ll)cost[i] * k + x, w + (ll)j * b));
}
}
}
for (int i = maxbird; i >= 0; i--) {
if (dp[n][i] != -1) {
return printf("%lld\n", i), 0;
}
}
return 0;
}
L - Knapsack
我们把 \(\operatorname{lcm}[1, 2, \dots, 8]\) 分为一组,即 \(840\)。
我们考虑剩下的。
剩下的可以跑一遍 DP。
然后最后统计答案时直接找到答案就可以了。
十年 OI 一场空,不开 long long 见祖宗。
#include <bits/stdc++.h>
using namespace std;
#define AKCoder
#ifdef AKCoder
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
#define debug(x) cout<<#x<<" = "<<(x)<<'\n'
#else
#define debug(x);
#endif
#define ll long long
#define ull unsigned long long
#define db double
#define all(x) (x).begin(), (x).end()
#define inf (1 << 30)
#define lnf (1LL << 60)
typedef pair<int, int> PII;
constexpr int N = 1000000000 + 7;
constexpr int P = 998244353;
ll W, cnt[10], dp[10][840 * 8 + 1000];
int main() {
scanf("%lld", &W);
for (int i = 1; i <= 8; i++) {
scanf("%lld", &cnt[i]);
}
memset(dp, -1, sizeof(dp));
dp[1][0] = 0;
for (int i = 1; i <= 8; i++) {
for (int j = 0; j <= 840 * 8; j++) {
if (dp[i][j] == -1) continue;
for (int k = 0; k <= 840 * 8 && j + k * i <= 840 * 8 && k <= cnt[i]; k++) {
dp[i + 1][j + k * i] = max(dp[i + 1][j + k * i], dp[i][j] + (cnt[i] - k) / (840 / i));
}
}
}
ll ans = 0;
for (int j = 0; j <= 840 * 8; j++) {
if (j > W || dp[9][j] == -1) continue;
ans = max<ll>(ans, j + 840LL * min<ll>(dp[9][j], (W - j) / 840));
}
printf("%lld\n", ans);
return 0;
}

浙公网安备 33010602011771号