Q: 如何判断几件物品能否被 2 辆车一次拉走?
A: DP 问题. 先 dp 求解第一辆车能够装下的最大的重量, 然后计算剩下的重量之和是否小于第二辆车的 capacity, 若小于, 这 OK.
Description
Emma and Eric are moving to their new house they bought after returning from their honeymoon. Fortunately, they have a few friends helping them relocate. To move the furniture, they only have two compact cars, which complicates everything a bit. Since the furniture does not fit into the cars, Eric wants to put them on top of the cars. However, both cars only support a certain weight on their roof, so they will have to do several trips to transport everything. The schedule for the move is planed like this:
Note, that the group is always staying together so that they can have more fun and nobody feels lonely. Since the distance between the houses is quite large, Eric wants to make as few trips as possible.
Given the weights wi of each individual piece of furniture and the capacities C1 and C2 of the two cars, how many trips to the new house does the party have to make to move all the furniture? If a car has capacity C, the sum of the weights of all the furniture it loads for one trip can be at most C.
Input
The first line contains the number of scenarios. Each scenario consists of one line containing three numbers n, C1 and C2. C1 and C2 are the capacities of the cars (1 ≤ Ci ≤ 100) and n is the number of pieces of furniture (1 ≤ n ≤ 10). The following line will contain n integers w1, …, wn, the weights of the furniture (1 ≤ wi ≤ 100). It is guaranteed that each piece of furniture can be loaded by at least one of the two cars.
Output
The output for every scenario begins with a line containing “Scenario #i:”, where i is the number of the scenario starting at 1. Then print a single line with the number of trips to the new house they have to make to move all the furniture. Terminate each scenario with a blank line.
Sample Input
2 6 12 13 3 9 13 3 10 11 7 1 100 1 2 33 50 50 67 98
Sample Output
Scenario #1: 2 Scenario #2: 3
思路:
总结:
代码:
#include <iostream> using namespace std; const int INF = 0X3F3F3F3F; const int MAXN = 10 + 1<<11; int N, C1, C2; int a[15]; bool dp[MAXN]; int dp2[MAXN]; int state[MAXN]; bool check(const int &s) { memset(dp, 0, sizeof(dp)); dp[0] = true; int sum = 0; for(int i = 0; i < N; i ++) { if(s & (1<<i)) { // 第 i 位为 1 sum += a[i]; for(int j = C1; j >= a[i]; j--) { // 背包的容量是 C1, 01背包逆序遍历 if(dp[j-a[i]]) dp[j] = true; } } } for(int i = 0; i <= sum; i++) { // 本来写成 C1 if(dp[i] && sum-i <= C2) { return true; } } return false; } int solve_dp() { memset(state, 0, sizeof(state)); int len = 0; int inf = 1<<N; for(int i = 0; i <= inf; i ++) { // <= if(check(i)) state[len++] = i; } // 第二次背包 memset(dp2, 0x3f, sizeof(dp2)); // 初始化为 INF dp2[0] = 0; for(int i = 0; i < len; i ++) { for(int j = inf; j >= 0; j --) { if(!(j & (state[i]))) { dp2[j|state[i]] = min(dp2[j|state[i]], dp2[j]+1); } } } return dp2[(1<<N)-1]; } int main() { freopen("E:\\Copy\\ACM\\测试用例\\in.txt", "r", stdin); int testcase; cin >> testcase; int tc = 0; while(testcase--) { tc++; scanf("%d%d%d", &N, &C1, &C2); for(int i = 0; i < N; i ++) { scanf("%d", &a[i]); } // solve printf("Scenario #%d:\n%d\n\n", tc, solve_dp()); } return 0; }
update 2014年3月14日15:30:07
1. 总结 (5), 为什么不添加判断会超时? 假如不添加判断, 那么当 j&state[i] != 0 时也会执行状态转移方程. 考虑状态 state[k] 是 state[i] 的子集, 同时 j&state[k] == 0, 那么
j|state[i] == j|state[j] 同时 dp[j|state[i]] >= dp[j|state[k]], 所以对 j&state[i] != 0 的 case, 直接 continue 就好