初始有 n 份文檔,需減少到 m 份。L 家公司提供服務:每家有兩種操作——單份處理費用 A,批量減半費用 B(將當前文檔數減半,但不得低於 m)。求各公司最小總費用,按費用升序、名字字典序輸出。
對於單一公司,最優策略:
證明:批量減半單位成本 = B / (cur/2) = 2B/cur。單份單位成本 = A。當 2B/cur ≤ A 即 B ≤ (cur/2)*A 時批量更優。且減半操作越早做曲線越陡(曲線凸),貪心選擇當前最優即全局最優。
#include
#include
#include
using namespace std;
const int N = 105;
struct Company {
char name[N];
int cost;
int a, b;
} com[N];
char str[N];
int cmp(const Company& x, const Company& y) {
if (x.cost != y.cost) return x.cost < y.cost;
return strcmp(x.name, y.name) < 0;
}
void parse(int idx) {
int k = 0;
while (str[k] && str[k] != ':') k++;
strncpy(com[idx].name, str, k);
com[idx].name[k] = '\0';
int sum = 0;
for (int i = k+1; str[i]; i++) {
if (str[i] != ',') sum = sum*10 + str[i]-'0';
else { com[idx].a = sum; sum = 0; }
}
com[idx].b = sum;
}
void calc_cost(int idx, int n, int m) {
int cur = n, total = 0;
while (1) {
if (cur/2 >= m && (cur - cur/2)*com[idx].a >= com[idx].b) {
total += com[idx].b;
cur /= 2;
} else {
total += (cur - m) * com[idx].a;
break;
}
}
com[idx].cost = total;
}
int main() {
int T;
scanf("%d", &T);
for (int tc = 1; tc <= T; tc++) {
int n, m, l;
char ch;
scanf("%d%d%d%c", &n, &m, &l, &ch);
for (int i = 0; i < l; i++) {
gets(str);
parse(i);
calc_cost(i, n, m);
}
sort(com, com+l, cmp);
printf("Case %d\n", tc);
for (int i = 0; i < l; i++)
printf("%s %d\n", com[i].name, com[i].cost);
}
return 0;
}
| 細節 | 處理方法 |
|---|---|
| 輸入解析 | 公司名含空格,用 `:` 分隔;費用用 `,` 分隔 |
| 減半限制 | 嚴格判斷 `cur/2 >= m`,不可低於 m |
| 排序規則 | 費用升序,同費用按名字字典序 |
| 大數據 | n ≤ 10⁹,int 足夠;費用可能超 int,建議用 long long |
Input: 1 100 10 2 CompanyA:10,50 CompanyB:5,200 Output: Case 1 CompanyA 150 CompanyB 450
CompanyA: 100→50(B=50)→25(B=50)→12(B=50)→10 單份(2×10=20) = 220? 待驗證...
經典貪心題,核心在於證明「當前最優選擇導致全局最優」。關鍵不等式 `(cur - cur/2)*A >= B` 即「減少的文檔數 × 單份成本 ≥ 批量成本」。實現時注意整數除法向下取整、邊界條件、輸入解析健壯性。