题目链接:http://poj.org/problem?id=2923
题目大意:给定n个物品和两辆车的最大运载量,每次两辆车都要同时开动,问最少开几次能把所有物品运走,1<=n<=10.
解题思路:好题,解法为状态压缩DP+背包,我的状态压缩DP做的特别少,所有在看到本题的n的范围时还没能很敏感地往状态压缩方面想。本题的解题思路是先枚举选择若干个时的状态,总状态量为1<<n,判断这些状态集合里的那些物品能否一次就运走,如果能运走,那就把这个状态看成一个物品。预处理完能从枚举中找到tot个物品,再用这tot个物品中没有交集(也就是两个状态不能同时含有一个物品)的物品进行01背包,每个物品的体积是state[i],价值是1,求包含n个物品的最少价值也就是dp[(1<<n)-1](dp[i]表示状态i需要运的最少次数)。
状态转移方程:dp[j|k] = min(dp[j|k],dp[k]+1) (k为state[i,1<=j<=(1<<n)-1])。算法复杂度O((2^N)*N)
测试数据:
10
代码:
#include <stdio.h> #include <string.h> #define MAX 2100 #define INF 0xfffffff #define min(a,b) (a)<(b)?(a):(b) int state[MAX],dp[MAX]; int n,m1,m2,ans,tot,cost[MAX]; int Ok(int x) { int i,j,k,sum = 0; int vis[MAX]; memset(vis,0,sizeof(vis)); for (vis[0] = 1,i = 0; i < n; ++i) if ((1<<i) & x) { sum += cost[i]; for (j = m1; j >= cost[i]; --j) if (vis[j-cost[i]]) vis[j] = 1; } for (i = 0; i <= m1; ++i) if (vis[i] && sum - i <= m2) return 1; return 0; } int main() { int i,j,k,t,cas = 0; scanf("%d",&t); while (t--) { scanf("%d%d%d",&n,&m1,&m2); for (i = 0; i < n; ++i) scanf("%d",&cost[i]); for (i = 0; i < 1<<n; ++i) dp[i] = INF; dp[0] = tot = ans = 0; for (i = 1; i <= (1<<n) - 1; ++i) if (Ok(i)) state[tot++] = i; for (i = 0; i < tot; ++i) for (j = (1<<n)-1; j >= 0; --j) { if (dp[j] == INF) continue; int tp = j + state[i]; if (tp != (j | state[i])) continue; dp[tp] = min(dp[tp],dp[j]+1); } printf("Scenario #%d:\n%d\n\n",++cas,dp[(1<<n)-1]); } }
本文ZeroClock原创,但可以转载,因为我们是兄弟。