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;  
}
posted @ 2026-07-22 16:51  AKCoder  阅读(7)  评论(0)    收藏  举报