有 n(≤ 10)件物品,每件有重量 a[i]。兩輛載重分別為 m1、m2 的卡車,每次可裝載任意子集,要求子集總重 ≤ m1 且剩餘重量 ≤ m2(即可一次運完)。求將所有物品運走的最少次數。
2^n 種狀態,判斷該子集是否能一次運完。
dp[state] = 1,並記錄該狀態備用。dp[s] = min(dp[s], dp[s ^ sub] + dp[sub]),其中 sub 遍歷 s 的所有非空子集。for (int i = s; i; i = (i-1) & s) 高效枚舉子集。dp[(1<。 #include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 12;
const int INF = 0x3f3f3f3f;
int n, m1, m2;
int a[MAXN];
bool vis[MAXN];
int dp[1 << MAXN];
// 判斷子集 x 能否一次運完
bool ok(int x) {
int sum = 0;
memset(vis, 0, sizeof(vis));
vis[0] = true;
for (int i = 0; i < n; i++) if (x & (1 << i)) {
sum += a[i];
for (int j = m1; j >= a[i]; j--) vis[j] |= vis[j - a[i]];
}
for (int i = 0; i <= m1; i++)
if (vis[i] && sum - i <= m2) return true;
return false;
}
int main() {
int T; scanf("%d", &T);
for (int cas = 1; cas <= T; cas++) {
scanf("%d%d%d", &n, &m1, &m2);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
int total = 1 << n;
fill(dp, dp + total, INF);
dp[0] = 0;
for (int i = 1; i < total; i++) {
if (ok(i)) dp[i] = 1;
}
for (int s = 1; s < total; s++) {
for (int sub = s; sub; sub = (sub - 1) & s) {
if (dp[sub] == INF) continue;
dp[s] = min(dp[s], dp[s ^ sub] + dp[sub]);
}
}
printf("Scenario #%d:\n%d\n\n", cas, dp[total - 1]);
}
return 0;
}
vis 記錄可達重量。for (int sub = s; sub; sub = (sub-1) & s) 是經典寫法,務必熟練。