多重背包习题题解(洛谷题单)

本篇对应源码见仓库 solutions/xcpc/algorithm/dp/knapsack-bounded/。

题单

P1833 樱花

题意

是基础的弱化的多重背包问题,数据不强,用朴素解法也能过。

思路

模板题,注意处理 \(c = 0\) 的情况,可以根据数据范围确定上界。

示例代码

signed main() {
    //IOS;
	int hh, mm;
	int T;
	scanf("%d:%d", &hh, &mm);
	T = hh * 60 + mm;
	scanf("%d:%d", &hh, &mm);
	T = hh * 60 + mm - T;
	int n;
	scanf("%d", &n);
	vector<int> w(N), v(N);

	int cnt = 0;
	fer(i, 0, n) {
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);

		if(!c) c = T / a + 1;

		int k = 1;
		while(k <= c) {
			w[++cnt] = k * a;
			v[cnt] = k * b;
			c -= k;
			k <<= 1;
		}
		if(c) {
			w[++cnt] = c * a;
			v[cnt] = c * b;
		}
	}
	vector<int> dp(1010);
    fer(i, 1, cnt + 1) {
    	ferd(j, T, w[i]) {
    		dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
    	}
    }
    cout << dp[T] << '\n';
    return 0;
}

P5365 SNOI2017 英雄联盟 P1077 摆花

题意

多重背包中恰好装满的方案数问题。

思路

不能直接用二进制优化解决,因为会重复计算同一类型物品对方案的贡献。由于数据范围较小,可以直接用三维遍历朴素解法。

示例代码

signed main() {
    IOS;

    int n, m;
    cin >> n >> m;
    vector<int> a(n + 1);
    fer(i, 1, n + 1) cin >> a[i];

    vector<int> dp(m + 1);
    dp[0] = 1;
    fer(i, 1, n + 1) {
    	ferd(j, m, 1) {
    		fer(k, 1, a[i] + 1) {
    			if(j >= k) dp[j] = (dp[j] + dp[j - k]) % MOD;
    		}
    	}
    }
    cout << dp[m] << '\n';

    return 0;
}

P2347 砝码称重

题意

设有 1g、2g、3g、5g、10g、20g 的砝码各若干枚(其总重 ≤1000),可以表示成多少种重量?

思路

是可行性分析的多重背包问题。通过得知各种砝码的个数得到背包最大容量,然后二进制优化成01背包进行 \(dp\) 即可。

注意
不可以直接用某个常数(比如1000)替代背包的最大容量,当砝码总重远小于 1000 时,多余的 dp 状态(即 j 大于 maxN 的部分)根本不应该更新,却仍然参与了状态转移,可能导致错误的状态传递和计数。

示例代码

signed main() {
    IOS;

    vector<int> w(N);

    int cnt = 0;
    int maxN = 0;
    fer(i, 1, 7) {
        int v;
        if(i <= 3) v = i;
        else if(i == 4) v = 5;
        else if(i == 5) v = 10;
        else if(i == 6) v = 20;

        int c;
        cin >> c;

        maxN += v * c;
        int k = 1;
        while(k <= c) {
            w[++cnt] = k * v;
            c -= k;
            k <<= 1;
        }
        if(c) {
            w[++cnt] = k * v;
            c -= k;
        }
    }

    // dp[j]: 重量j的可能性
    vector<bool> dp(maxN + 1);
    dp[0] = 1;

    fer(i, 1, cnt + 1) {
        ferd(j, maxN, w[i]) {
            dp[j] = dp[j] | dp[j - w[i]];
        }
    }
    int ans = 0;
    fer(i, 1, maxN + 1)  ans += dp[i];
    cout << "Total=" << ans << '\n';
    return 0;
}

P1776 宝物筛选

题意

二进制优化的多重背包的模板题。

思路

二进制优化模板题。

示例代码

signed main() {
    IOS;

    int n, W;
    cin >> n >> W;

    vector<int> a(N), b(N);

    int cnt = 0;
    fer(i, 0, n) {
    	int v, w, m;
    	cin >> v >> w >> m;

    	int k = 1;
    	while(k <= m) {
    		a[++cnt] = v * k;
    		b[cnt] = w * k;
    		m -= k;
    		k <<= 1;
    	}
    	if(m) {
    		a[++cnt] = v * m;
    		b[cnt] = w * m;
    	}
    }
    vector<int> dp(W + 1);
    fer(i, 1, cnt + 1) {
    	ferd(j, W, b[i]) {
    		dp[j] = max(dp[j], dp[j - b[i]] + a[i]);
    	}
    }
    cout << dp[W] << '\n';

    return 0;
}

P1782 旅行商的背包

题意

物品有两大种,一种是每种物品有对应的体积、价值、个数;另一种是“奇货”,第 i 件的价值 \(Yi\) 与分配的体积 \(Xi\) 之间的关系为:\(Yi=aiXi2+biXi+ci\) 。求背包容量为 \(C\) 的最大价值。

思路

前一部分显然是多重背包,后一部分其实是一个朴素的 \(dp\) 问题,先枚举背包容量,在枚举考虑的奇货的体积。

注意 !

对于奇货的 \(dp\) ,可以先枚举奇货体积,再枚举背包容量吗?
答案是不能的!
如果这样的话,会导致在更新过程中会用到已经更新过的 dp 值,导致同一个“奇货”被“重复使用”了(即多次分配体积),从而破坏了 0/1 背包的正确性。
而原来的遍历方式,先遍历了背包的容量,再遍历奇货体积,保证了对当前奇货的决策只基于上一个阶段(即还未加入当前奇货)的 dp 状态。这样就能正确地选择对当前奇货只做一次分配,从而避免重复使用。

示例代码

signed main() {
    IOS;

    int n, m, C;
    cin >> n >> m >> C;

    vector<int> V(N), W(N);
    vector<ll> dp(C + 1);
    int cnt = 0;

    fer(i, 0, n) {
    	int v, w, d;
    	cin >> v >> w >> d;

    	int k = 1;
    	while(k <= d) {
    		V[++cnt] = k * v;
    		W[cnt] = k * w;
    		d -= k;
    		k <<= 1;

    		ferd(j, C, V[cnt]) {
    			dp[j] = max(dp[j], dp[j - V[cnt]] + W[cnt]);
    		}
    	}
    	if(d) {
    		V[++cnt] = d * v;
    		W[cnt] = d * w;
    		ferd(j, C, V[cnt]) {
    			dp[j] = max(dp[j], dp[j - V[cnt]] + W[cnt]);
    		}
    	}
    }
    fer(i, 0, m) {
    	int a, b, c;
    	cin >> a >> b >> c;

    	// fer(v, 1, C + 1) {
        //     ll w = 1LL * a * v * v + b * v + c;
        //     ferd(j, C, v) {
        //         dp[j] = max(dp[j], dp[j - v] + w);
        //     }
        // }
        ferd(j, C, 0) {
            fer(v, 0, j + 1) {
                ll w = 1LL * a * v * v + b * v + c;
                dp[j] = max(dp[j], dp[j - v] + w);
            }
        }
    }
    cout << dp[C] << '\n';
    return 0;
}
posted @ 2026-11-11 06:21  slowbyte  阅读(3)  评论(0)    收藏  举报