POJ 1948 給定 n (≤40) 根木棒,長度 ≤40,選擇若干根組成三角形,求最大面積。每根木棒只能用一次,不可切割。
a, b, c 滿足:a+b>c, a+c>b, b+c>a(等價於任意兩邊之和大於第三邊)P = a+b+c 固定為所選木棒總長度S = √[p(p-a)(p-b)(p-c)],p = P/2a,b 確定,則 c = P - a - bdp[i][j]:是否能用部分木棒組成兩堆,長度分別為 i 和 j(第三堆隱含為 sum - i - j)。
i, j ≤ 800(最大周長 40×40=1600,半周長 800)dp[801][801],反向迴圈避免重複使用同一根木棒對於每根長度為 L 的木棒:
for i from 800 down to 0:
for j from i down to 0: // 利用對稱性 j ≤ i
if i >= L and dp[i-L][j] dp[i][j] = true; // 放入第一堆
if j >= L and dp[i][j-L] dp[i][j] = true; // 放入第二堆
double ans = 0;
for i = 0..800:
for j = 0..i:
if dp[i][j]:
k = sum - i - j;
if (i+j>k && i+k>j && j+k>i) { // 三角形不等式
double p = (i+j+k)/2.0;
double area = sqrt(p*(p-i)*(p-j)*(p-k));
ans = max(ans, area);
}
#include <bits/stdc++.h>
using namespace std;
bool dp[801][801];
int sticks[45];
double area(int a, int b, int c) {
double p = (a + b + c) / 2.0;
return sqrt(p * (p-a) * (p-b) * (p-c));
}
bool valid(int a, int b, int c) {
return a+b>c && a+c>b && b+c>a;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
while (cin >> n) {
int sum = 0;
for (int i = 0; i < n; ++i) {
cin >> sticks[i];
sum += sticks[i];
}
memset(dp, 0, sizeof(dp));
dp[0][0] = true;
for (int idx = 0; idx < n; ++idx) {
int L = sticks[idx];
for (int i = 800; i >= 0; --i) {
for (int j = i; j >= 0; --j) { // j <= i 利用對稱性
if (i >= L && dp[i-L][j]) dp[i][j] = true;
if (j >= L && dp[i][j-L]) dp[i][j] = true;
}
}
}
double ans = 0;
for (int i = 0; i <= 800; ++i) {
for (int j = 0; j <= i; ++j) {
if (dp[i][j]) {
int k = sum - i - j;
if (k < 0) continue;
if (valid(i, j, k)) {
ans = max(ans, area(i, j, k));
}
}
}
}
if (ans == 0) cout << "-1\n";
else cout << int(ans * 1000) / 10.0 << '\n'; // 保留一位小數
}
return 0;
}
j ≤ i,狀態數減半int(ans*1000)/10.0 避免浮點誤差bitset 優化或贪心+搜索此題完美展示了「二維背包 + 幾何性質 + 狀態壓縮」的綜合應用,是背包問題進階的經典範例。