01 背包的变形
消失之物
链接:https://www.luogu.com.cn/problem/P4141
题目描述
ftiasch 有 \(n\) 个物品, 体积分别是 \(w_1,w_2,\dots,w_n\)。由于她的疏忽,第 \(i\) 个物品丢失了。
“要使用剩下的 \(n-1\) 物品装满容积为 \(x\) 的背包,有几种方法呢?”——这是经典的问题了。
她把答案记为 \(\text{cnt}(i,x)\) ,想要得到所有\(i \in [1,n]\), \(x \in [1,m]\) 的 \(\text{cnt}(i,x)\) 表格。

输入格式
第一行两个整数 \(n,m\),表示物品的数量和最大的容积。
第二行 \(n\) 个整数 \(w_1,w_2,\dots,w_n\),表示每个物品的体积。
输出格式
输出一个 \(n \times m\) 的矩阵,表示 \(\text{cnt}(i,x)\) 的末位数字。
样例 #1
样例输入 #1
3 2
1 1 2
样例输出 #1
11
11
21
提示
【数据范围】
对于 \(100\%\) 的数据,\(1\le n,m \le 2000\),且 \(1\le v_i\le m\)。
【样例解释】
如果物品 3 丢失的话,只有一种方法装满容量是 2 的背包,即选择物品 1 和物品 2。
\(\text{upd 2023.8.11}\):新增加五组 Hack 数据。
解答
- f[j][0]:表示未删物品前的,组成体积为- j的方案数
- f[j][1]:表示删除某个物品,组成体积为- j的方案数
- 前者就是一个 01背包问题
- 后者状态转移主要是 f[j][1] = f[j][0] - f[j - v[i]][1]
- 解释:因为f[j][0]表示未删,多了部分,需要删除一些,所以枚举每个物品,表示可能删除的,假设当前物品为i,f[j - v[i]][1]表示的是组成体积j - v[i]且前面已经删除一个数了
- 而这个体积只要加上 v[i],也就是选这个i物品,便可组成体积j,我们枚举的也就是删除i物品的方案,所以需要删除这一部分
- 下述 %10的原因是题目要求输出末尾数字即可
#include <iostream>
#include <cstring>
using namespace std;
const int N = 2010;
int v[N];
int f[N][2];
int n, m;
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++) cin >> v[i];
	f[0][0] = f[0][1] = 1;
	for(int i = 1; i <= n; i++)
		for (int j = m; j >= v[i]; j--)
		{
			f[j][0] += f[j - v[i]][0];
			f[j][0] %= 10;
		}
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
		{
			if (j >= v[i]) f[j][1] = (f[j][0] - f[j - v[i]][1] + 10)  % 10;
			else f[j][1] = f[j][0];
			cout << f[j][1];
		}
		cout << endl;
	}
	
	return 0;
}

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号