20240302 專項訓練

hayzxjr發表於2024-03-06

揹包專項訓練

bottle

題意簡述

link
\(n\) 瓶水,第 \(i\) 瓶水有剩餘水量 \(a_i\) 和最大容積 \(b_i\),在不超過瓶子容積的前提下,小 A 可以把任意多的水從一個瓶子倒向另一個瓶子,所花費的時間等同於倒過去的水的體積。

求最多能得到多少個空瓶,以及在得到最多的空瓶的前提下,他最少需要花費的時間。

對於 \(70\%\) 的資料,\(1 \le n, a_i, b_i \le 100\)

對於 \(100\%\) 的資料,\(1 \le n, a_i, b_i \le 500\)

簡要分析

題意簡化為:選出若干個瓶子,使得選出的瓶子最多,且 總水量 \(\le\) 未被選中的瓶子的總容積,同時要求選中的瓶子的 原有 水量最少。

\(\sum a_i\) 為被選為空瓶的總水量,\(\sum a_j\) 為剩餘瓶子的總水量,不難得到 \(\sum a_i + \sum a_j = \sum a\),對 \(b\) 同樣如此定義。

題意即可形式化為:使 \(\sum a_i\) 最小,同時保證 \(\sum b_j \ge \sum a\)

70 pts

對於 \(70\%\) 的資料,\(1 \le n, a_i, b_i \le 100\)。資料範圍啟發做法,可嘗試 \(\mathcal{O}(n^4)\) 的演算法。

首先考慮求出最多空瓶數,貪心做法。容積小的瓶子優先選擇。因為要滿足 \(\sum b_j \ge \sum a\),顯然貪心為真。

然後考慮第二個答案。記 \(f_{i, j}\) 表示用掉 \(i\) 個瓶子 浪費 了容積 \(j\) 所需的最小 \(\sum a_i\)。轉移 \(f_{i, j} = \min_{k \in [i, n]}{f_{i - 1, j - b_k} + a_k}\)

由於要列舉當前走到的位置(\(\mathcal{O}(n)\)),內層列舉用掉的瓶子數(\(\mathcal{O}(n)\)),接著列舉浪費的容積(\(\mathcal{O}(n^2)\)),因此複雜度為 \(\mathcal{O}(n^4)\)

100 pts

\(f_i\) 表示

#include<bits/stdc++.h>
#define N 505
#define M 250005
using namespace std;
int read(){
	int x = 0, f = 1; char ch = getchar();
	while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
	while(isdigit(ch)) {x = (x << 3) + (x << 1) + ch - 48; ch = getchar();}
	return x * f;
}
int n, sum, a[N], b[N], f[M], g[M];
int main(){
	n = read();
	for(int i = 1; i <= n; i++) a[i] = read(), sum += a[i];
	for(int i = 1; i <= n; i++) b[i] = read();
	memset(g, 0x3f, sizeof g);
	int Max = 250000; g[0] = 0;
	for(int i = 1; i <= n; i++){
		for(int k = Max; k >= b[i]; k--){
			if(g[k - b[i]] + 1 < g[k])
			  f[k] = f[k - b[i]] + a[i],
			  g[k] = g[k - b[i]] + 1;
			else if(g[k - b[i]] + 1 == g[k])
			  f[k] = max(f[k], f[k - b[i]] + a[i]);
		}
	}
	int ans1 = INT_MAX, ans2 = -INT_MAX;
	for(int i = sum; i <= Max; i++){
		if(g[i] < ans1) ans1 = g[i], ans2 = f[i];
		else if(g[i] == ans1) ans2 = max(ans2, f[i]);
	}
	printf("%d %d\n", n - ans1, sum - ans2);
	return 0;
}

file

link
小 A 有 \(𝑛\) 份檔案需要儲存,對於第 \(𝑖\) 份檔案,他需要花費 \(𝑡_𝑖\) 的時間來儲存。

然而,第 \(𝑖\) 份檔案在經過 \(𝑑_𝑖\) 的時間之後就會被銷燬,若想要成功地儲存第 \(𝑖\) 份檔案,就必須要在時間 \(𝑑_𝑖\) 之前完成儲存,即使是恰好在𝑑𝑖時刻完成儲存也沒有意義。

因此,對於 \(𝑡_𝑖 \ge 𝑑_𝑖\) 的檔案,小 A 是無論如何也無法完成儲存的。

小 A 給第 \(𝑖\) 份檔案賦予了一個價值 \(𝑝_𝑖\),現在他想要知道他可能能儲存下來的最大價值。

請注意,小 A 在任意時刻都只能進行一份檔案的儲存,如果他先後儲存了 \(𝑎\) 檔案和 \(𝑏\) 檔案,那麼𝑎檔案完成儲存的時間就是 \(𝑡_𝑎\),而 \(𝑏\) 檔案完成儲存的時間是 \(𝑡_𝑎 + 𝑡_𝑏\)

對於 \(70\%\) 的資料,保證 \(1 \le 𝑛 \le 100,1 ≤ 𝑡_𝑖, 𝑝_𝑖≤ 20, 1 ≤ 𝑑_𝑖≤ 2000\)

對於 \(100\%\) 的資料,保證 \(1 ≤ 𝑛 ≤ 1000, 1 ≤ 𝑡_𝑖, 𝑝_𝑖≤ 100, 1 ≤ 𝑑_𝑖≤ 100000\)

相關文章