HDU2191 多重背包 三种解法

来源:互联网 发布:天音网络黄涛 编辑:程序博客网 时间:2024/04/28 07:09

Problem Address:http://acm.hdu.edu.cn/showproblem.php?pid=2191


【思路】


简单的多重背包。

但是多重背包并不简单。以下简单介绍多重背包的三种解法。


第一种:O(V*Σ(Ki))

这就是最平常的解法。把多重背包当做01背包来做。

把每一件物品的k个数量拆分为k个物品,然后进行01背包。


第二种:O(V*∑([log2Ki]))

这是前一个方法的升级。利用了二进制的思想。

把k拆分为1、2、4……2^(t-1)、Ki-2^t+1的数列,其中t是满足Ki-2^t+1>0的最大整数。

可以想象一下,比如7以内的任意数,是可以通过1、2、4这三个数组合而来。

以上两种方法在《背包九讲》中都有讲解。


第三种:O(VN)

这种方法利用了单调队列。可以先了解单调队列。

详细方法参考国家集训队论文《浅谈几类背包问题》。

达到了和01背包一样的时间复杂度。


【代码】



第一种:O(V*Σ(Ki))


#include <iostream>using namespace std;const int maxn = 100;int dp[maxn+5];int main(){int t;int n, m, p, h, c;int i, j, k;scanf("%d", &t);while(t--){memset(dp, 0, sizeof(dp));scanf("%d %d", &n, &m);for (i=0; i<m; i++){scanf("%d %d %d", &p, &h, &c);for (j=n; j>=0; j--){for (k=1; k<=c && j+k*p<=n; k++){if (dp[j]+k*h>dp[j+k*p])dp[j+k*p] = dp[j] + k*h;}}}printf("%d\n", dp[n]);}return 0;}



第二种:O(V*∑([log2Ki]))


#include <iostream>using namespace std;const int maxn = 100;int dp[maxn+5];int main(){int t;int n, m, p, h, c;int i, j, k, w, val;scanf("%d", &t);while(t--){memset(dp, 0, sizeof(dp));scanf("%d %d", &n, &m);for (i=0; i<m; i++){scanf("%d %d %d", &p, &h, &c);for (k=1; c-k*2+1>0; k*=2){w = k*p;val = k*h;for (j=n-w; j>=0; j--){if (dp[j]+val>dp[j+w])dp[j+w] = dp[j]+val;}}k = c - k + 1;w = k*p;val = k*h;for (j=n-w; j>=0; j--){if (dp[j]+val>dp[j+w])dp[j+w] = dp[j]+val;}}printf("%d\n", dp[n]);}return 0;}



第三种:O(VN)


#include <iostream>using namespace std;const int maxn = 100;int dp[maxn+5];int A[maxn+5];int B[maxn+5];int L, R;inline void insert(int a, int b)//插入一个position为a,值为b的元素到队列中{while(R>=L && b>B[R]) R--;R++;A[R] = a;B[R] = b;}int main(){int t;int n, m, p, h, c;int i, j;int d;scanf("%d", &t);while(t--){memset(dp, 0, sizeof(dp));scanf("%d %d", &n, &m);for (i=0; i<m; i++){scanf("%d %d %d", &p, &h, &c);for (d=0; d<p; d++){L = 1;R = 0;for (j=0; j<=(n-d)/p; j++){insert(j, dp[j*p+d]-j*h);if (A[L]<j-c)//如果队列的首元素已经失效L++;dp[j*p+d] = B[L] + j*h;}}}printf("%d\n", dp[n]);}return 0;}