POJ 3093 背包 (技巧优化)

来源:互联网 发布:减速箱装配图 淘宝 编辑:程序博客网 时间:2024/06/15 19:22

参考:09年集训队论文《浅谈几类背包问题》 徐持衡

题意:多组输入,每组给定物品数(<=30)和背包容量(<=1000)以及接下来每个物品的体积,问有多少种方案,使得装入一些物品后,无法装入剩下的任意一个物品。

可以转化成0-1背包来做,首先按体积从小到大排序,枚举“剩下的物品”中体积最小的。剩下的物品中体积最小的为i时,前i-1个物品是必然被装入背包的,然后对第i+1到第n个物品做0-1背包问题,转移方程f[i][j]表示前i个物品装入容量为j的背包的方案数,f[i][j] = f[i-1][j] + f[i-1][j-v[j]]。那么便有了这个O(T*n*n*C)的做法。

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;int T, n, m, sum, v[33];long long ans, f[1005];int main (){    scanf ("%d", &T);    for (int num = 1; num <= T; num++)    {        sum = ans = 0;        scanf ("%d %d", &n, &m);        for (int i = 1; i <= n; i++) scanf ("%d", v+i);        sort(v+1, v+n+1);        if (v[1] > m) {printf ("%d 0\n", num); continue;}        for (int i = 1; i < n; i++)        {            if (sum > m) break;            memset (f, 0, sizeof f);            f[sum] = 1;            for (int ii = i+1; ii <= n; ii++)            {                for (int j = m; j >= v[ii]+sum; j--)                    f[j] = f[j] + f[j-v[ii]];            }            int tmp = max(m-v[i]+1,sum);            for (int j = tmp; j <= m; j++) ans += f[j];            sum += v[i];        }        printf ("%d %lld\n", num, ans);    }    return 0;}

可以考虑一个优化。在之前的做法中,每个物品都被多次考虑装入背包,n-1次。稍微改一下细节做法,之前的做法是f[sum] = 1,背包容量仍然为C,可以改成f[0] = 1,背包容量为C-sum。那么就可以“倒着来”,在我们倒序枚举物品i装入背包的同时,也就相当于第i-1个物品为最小的剩下的物品,而i-2之前都被装入。对于背包容量的变化,每次直接从最大容量C开始考虑即可。这样复杂度就是O(T*n*C)。

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;int T, n, m, sum, v[33];long long ans, f[1005];int getint (){    int x = 0; char c = getchar();    while (c < '0' || c > '9') c = getchar();    while (c <= '9' && c >= '0') x = x*10+c-48, c = getchar();    return x;}int main (){    T = getint();    for (int num = 1; num <= T; num++)    {        sum = ans = 0;        memset (f, 0, sizeof f);        f[0] = 1;        n = getint(), m = getint();        for (int i = 1; i <= n; i++) sum += (v[i] = getint());        sort(v+1, v+n+1);        if (v[1] > m) {printf ("%d 0\n", num); continue;}        for (int i = n; i; i--)        {            sum -= v[i];            for (int j = 0; j < v[i] && m-sum-j >= 0; j++)                ans += f[m-sum-j];            for (int j = m; j >= v[i]; j--) f[j] += f[j-v[i]];        }        printf ("%d %lld\n", num, ans);    }    return 0;}
0 0
原创粉丝点击