開啟章節選單
13242 Pool Filling
題目連結
題目敘述
有一個空的泳池,需要以指定的溫度裝滿水
為此,在泳池邊緣放置了一排水罐,編號從 0 開始
每個水罐中的水量及水溫各不相同
你可以將任意多個水罐的水倒入泳池
但所選的水罐必須是連續編號的
目標是在不溢出的前提下,至少倒入泳池容量一半的水
並使最終混合水溫儘可能接近目標溫度
且混合後的水溫不得超出目標溫度上下 5 度
我們假設,將兩份水混合時,混合後的溫度為各自溫度以水量為權重的加權平均值
例如,若有 5 個水罐,其內容如下:
水罐編號 | 容量 (公升) | 溫度 (°C) |
---|---|---|
0 | 45 | 23 |
1 | 20 | 40 |
2 | 67 | 22 |
3 | 109 | 11 |
4 | 9 | 56 |
例如,你可以倒入水罐 1、2、3,但不能只倒入水罐 1 和 3
若泳池容量足夠,倒入水罐 1、2、3 的總水量為 20 + 67 + 109 = 196 公升
混合後的溫度為 (20×40+67×22+109×11)/(20+67+109) = 17.719388 °C
若沒有合適的方案則輸出 Not Possible
輸入格式
- 首行包含一個整數,表示待解決的問題數量。
- 對於每個問題,依序包含:
- 一行包含兩個整數,分別為游泳池的最大容量與目標溫度。
- 一行包含一個整數,表示水罐數量(不超過 3000)。
- 接著 N 行,每行包含兩個整數,分別為該水罐的水量與溫度。
輸出格式
- 對於每個問題,輸出一行:
- 若可行,輸出兩個整數,分別為倒入游泳池的首個水罐編號與末尾水罐編號,使混合後的水溫與目標溫度的差值最小。
- 若無法在不超出可接受溫度範圍(目標溫度 ±5°C)且至少填滿池塘一半容量的前提下達成,輸出
Not possible
。 - 若存在多種最優解,選擇編號最小的一組水罐。
解題思路
-
前綴和準備:為了快速計算任意連續區間的總體積與熱量,我們建立兩個前綴和陣列:
- a[i]:表示第 1 到第 i 罐水的總熱量(體積×溫度加總)。
- b[i]:表示第 1 到第 i 罐水的總體積。
-
暴力枚舉區間:使用兩層迴圈 i、j(1 ≤ i ≤ j ≤ n)枚舉所有可能的連續罐子區間。
-
計算區間 [i, j] 的總熱量 u = a[j] - a[i-1] 與總體積 d = b[j] - b[i-1]。
-
若 d > cap,表示超出池塘容量,可 break 提前終止內層迴圈,因為 j 再增加只會更大。
-
若 d >= ceil(cap/2)(至少填滿一半)且溫度誤差 |u/d - temp| <= 5,則比較當前誤差與歷史最小值,更新最優解。
-
-
結果輸出:若未找到合法區間,輸出 Not possible;否則輸出最優區間的左右端點。
程式碼
#include <bits/stdc++.h> using namespace std; signed main() { int t; // t:測試案例的數量 double cap, temp; // cap:池塘最大容量、temp:目標溫度 cin >> t; while (t--) { cin >> cap >> temp; // 讀取當前案例的池塘容量與目標溫度 int n; cin >> n; // n:瓶子數量 vector<pair<int, int>> v(n); // v[i] = {總熱量(volume*temperature),體積} vector<double> a(n+1), b(n+1); // a[i]:前 i 罐的總熱量前綴和;b[i]:前 i 罐的體積前綴和 for (auto &i : v) cin >> i.se >> i.first, i.first *= i.second; // 讀入每個瓶子的體積與溫度,並計算該瓶的熱量後存入 v // i.second:體積, i.first:溫度;然後將 i.first 換成熱量 = 溫度 * 體積 for (int i = 1; i <= n; i++) a[i] = a[i-1]+v[i-1].fi, b[i] = b[i-1]+v[i-1].second; // 建立前綴和: // a[i] = a[i-1] + v[i-1].first(累加熱量) // b[i] = b[i-1] + v[i-1].second(累加體積) pair<int, int> ans; // 儲存最佳解的左右端點 (0-base 索引) double mn = 1e9; // 初始化最小溫度誤差為極大值 for (int i = 1; i <= n; i++) { // 枚舉所有可能的連續區間 [i, j] for (int j = i; j <= n; j++) { double u = a[j]-a[i-1], d = b[j]-b[i-1]; // 計算區間 [i, j] 的總熱量 u 與總體積 d double dif = abs(u/d-temp); // 計算混合後溫度與目標溫度的絕對誤差 if (d > cap) break; // 若目前體積已經超出最大容量,內層迴圈後續 j 只會更大,可提前中斷 if (dif <= 5 && dif < mn && d >= ceil(cap/2)) // 條件:至少填滿一半容量、溫度誤差不超過 5,且若誤差更小,更新答案 mn = dif, ans = {i-1, j-1}; // 記錄 0-base 索引範圍,i-1 為起始、j-1 為結束 } } if (mn == 1e9) cout << "Not possible\n"; // 若 mn 未被更新,表示沒有符合條件的區間 else cout << ans.fi << ' ' << ans.se << '\n'; } }