01 揹包的變形

星竹z發表於2024-04-24

消失之物

連結: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]表示未刪,多了部分,需要刪除一些,所以列舉每個物品,表示可能刪除的,假設當前物品為 if[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;
}

相關文章