ZOJ 1409「通訊系統」是一道經典的貪心算法題目,核心在於最大化帶寬與價格的比值 B/P。由於 B 取決於所有設備中最小的帶寬,P 為所有設備總價格,因此我們需要讓分母 P 盡可能小、分子 B 盡可能大。本文將詳細解析如何結合枚舉與貪心策略高效求解。
題目要求最大化 B/P,其中 B 為所選設備中的最小帶寬。我們可以枚舉所有可能的帶寬值作為候選 B,對於每一個候選帶寬 band,檢查是否能為每類設備選擇一個帶寬 ≥ band 的廠家,並使總價格最小。若某類設備無滿足條件的廠家,則該 band 不可行,隨著 band 遞增,一旦不可行即可終止枚舉。
給定候選帶寬 band 後,對每類設備獨立決策:在所有帶寬 ≥ band 的廠家中,選擇價格最低者。這符合貪心選擇性質,局部最優(單類設備最便宜)能推導出全局最優(總價格最小)。
band 且價格最低的廠家時,可從高帶寬向低帶寬掃描,首個滿足條件者即為最優(因同帶寬下價格可能不同,需記錄最小值)。band 導致某類設備無可用廠家,後續更大的 band 必然也無解,直接結束。設 n 為設備類數,m 為最大廠家數,總帶寬種類數 ≤ n×m。枚舉帶寬 O(n×m),每次檢查所有設備 O(n×m),總複雜度 O(n²m²)。實測在題目限制下僅需 0ms,極其高效。
#include <cstdio>
#include <algorithm>
using namespace std;
const int INF = 1 << 28;
const int MAXN = 102;
const int MAXM = 10002;
struct Device {
int band, price;
} dev[MAXN][MAXN];
int bandList[MAXM];
int devCount[MAXN];
int n, m;
int solve(int targetBand) {
int totalPrice = 0;
for (int i = 1; i <= n; ++i) {
int minPrice = INF;
for (int j = 1; j <= devCount[i]; ++j) {
if (dev[i][j].band >= targetBand)
minPrice = min(minPrice, dev[i][j].price);
}
if (minPrice == INF) return -1; // 無可用廠家
totalPrice += minPrice;
}
return totalPrice;
}
bool cmpBandDesc(const Device& a, const Device& b) {
return a.band > b.band;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
int bandTop = 0;
for (int i = 1; i <= n; ++i) {
scanf("%d", &m);
devCount[i] = m;
for (int j = 1; j <= m; ++j) {
scanf("%d%d", &dev[i][j].band, &dev[i][j].price);
bandList[++bandTop] = dev[i][j].band;
}
sort(dev[i] + 1, dev[i] + m + 1, cmpBandDesc);
}
sort(bandList + 1, bandList + bandTop + 1);
bandTop = unique(bandList + 1, bandList + bandTop + 1) - bandList - 1;
double bestRatio = 0.0;
for (int i = 1; i <= bandTop; ++i) {
int price = solve(bandList[i]);
if (price == -1) break;
double ratio = bandList[i] * 1.0 / price;
if (ratio > bestRatio) bestRatio = ratio;
}
printf("%.3lf\n", bestRatio);
}
return 0;
}
本題展示了「枚舉決策邊界 + 貪心局部最優」的經典組合。關鍵在於識別 B 由最小帶寬決定,從而將連續優化問題轉化為離散枚舉,再利用排序與剪枝大幅降低實際運行時間。這類思路廣泛適用於「最大化比值且分子為最小值」的問題。