POJ 多重背包专题

POJ 1014 Dividing

这道题用背包做有两种解法,一种是拆分法,另一种是很神的O(VN)的DP法。

拆分法:

#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <cmath>
#include <ctime>
#define MAXN 10005
#define INF 100000000
#define eps 1e-11
#define L(x) x<<1
#define R(x) x<<1|1
using namespace std;
int f[240005], v;
void complete_pack(int *a, int c, int w)
{
    for(int i = c; i <= v; i++)
        a[i] = max(a[i], a[i - c] + w);
}
void zeroone_pack(int *a, int c, int w)
{
    for(int i = v; i >= c; i--)
        a[i] = max(a[i], a[i - c] + w);
}
void mutiple_pack(int *a, int c, int w, int M)
{
    if(c * M >= v)
    {
        complete_pack(a, c, w);
        return;
    }
    int k = 1;
    while(k < M)
    {
        zeroone_pack(a, k * c, k * w);
        M = M - k;
        k = 2 * k;
    }
    zeroone_pack(a, c * M, w * M);
}
int main()
{
    //freopen("d:/data.in","r",stdin);
    //freopen("d:/data.out","w",stdout);
    int sum, i, c[7], w[7], m[7],cas = 0;
    while(scanf("%d%d%d%d%d%d", &m[1], &m[2], &m[3], &m[4], &m[5], &m[6]))
    {
        if(m[1] == 0 && m[2] == 0 && m[3] == 0 && m[4] == 0 && m[5] == 0 && m[6] == 0)
        break;
        sum = 0;
        for(i = 1; i <= 6; i++)
        {
            c[i] = w[i] = i;
            sum += c[i] * m[i];
        }
        printf("Collection #%d:\n", ++cas);
        if(sum & 1)
        {
            puts("Can't be divided.\n");
        }
        else
        {
            sum /= 2;
            for(i = 1; i <= sum; i++)
            f[i] = -INF;
            f[0] = 0;
            v = sum;
            for(i = 1; i <= 6; i++)
            mutiple_pack(f, c[i], w[i], m[i]);
            if(f[v] < 0)
            {
                puts("Can't be divided.\n");
            }
            else
            {
                puts("Can be divided.\n");
            }
        }
    }
    return 0;
}



然后是O(VN)的方法,因为本题是一种背包的可行性问题,所以才能用这种方法。

#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <cmath>
#include <ctime>
#define MAXN 10005
#define INF 100000000
#define eps 1e-11
#define L(x) x<<1
#define R(x) x<<1|1
using namespace std;
int f[240005], v, used[240005];
int main()
{
    //freopen("d:/data.in","r",stdin);
    //freopen("d:/data.out","w",stdout);
    int sum, i, c[7], w[7], m[7],cas = 0, j;
    while(scanf("%d%d%d%d%d%d", &m[1], &m[2], &m[3], &m[4], &m[5], &m[6]))
    {
        if(m[1] == 0 && m[2] == 0 && m[3] == 0 && m[4] == 0 && m[5] == 0 && m[6] == 0)
        break;
        sum = 0;
        for(i = 1; i <= 6; i++)
        {
            c[i] = w[i] = i;
            sum += c[i] * m[i];
        }
        printf("Collection #%d:\n", ++cas);
        if(sum & 1)
        {
            puts("Can't be divided.\n");
        }
        else
        {
            sum /= 2;
            memset(f, 0, sizeof(f));
            f[0] = 1;
            for(i = 1; i <= 6; i++)
            {
                for(j = 0; j <= sum; j++)
                used[j] = 0;
                for(j = c[i]; j <= sum; j++)
                {
                    if(!f[j] && f[j - c[i]] && used[j - c[i]] + 1 <= m[i])
                    {
                        f[j] = 1;
                        used[j] = used[j - c[i]] + 1;
                    }
                }
            }
            if(f[sum])
            {
                puts("Can be divided.\n");
            }
            else
            puts("Can't be divided.\n");
        }
    }
    return 0;
}

POJ 1742Coins

很裸的背包可行性问题

#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <cmath>
#include <ctime>
#define MAXN 10005
#define INF 100000000
#define eps 1e-11
#define L(x) x<<1
#define R(x) x<<1|1
using namespace std;
bool f[100005];
int used[100005];
int main()
{
    //freopen("d:/data.in","r",stdin);
    //freopen("d:/data.out","w",stdout);
    int n, m, v[105], num[105], i, j;
    while(scanf("%d%d", &n, &m) != EOF)
    {
        if(n == 0 && m == 0) break;
        for(i = 1; i <= n; i++)
        scanf("%d", &v[i]);
        for(i = 1; i <= n; i++)
        scanf("%d", &num[i]);
        memset(f, 0, sizeof(f));
        f[0] = 1;
        int sum = 0;
        for(i = 1; i <= n; i++)
        {
            memset(used, 0, sizeof(used));
            for(j = v[i]; j <= m; j++)
            {
                if(!f[j] && f[j - v[i]] && used[j - v[i]] + 1 <= num[i])
                {
                    f[j] = 1;
                    used[j] = used[j - v[i]] + 1;
                    sum++;
                }
            }
        }
        printf("%d\n", sum);
    }
    return 0;
}


POJ 2392 Space Elevator

这个题的多了一个限制条件,实际上只要按限制条件从小到大排序就行

#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <cmath>
#include <ctime>
#define MAXN 10005
#define INF 100000000
#define eps 1e-11
#define L(x) x<<1
#define R(x) x<<1|1
using namespace std;
struct wwj
{
    int h, num, a;
}p[405];
bool f[400005];
int used[400005];
bool cmp(wwj x, wwj y)
{
    return x.a < y.a;
}
int main()
{
    //freopen("d:/data.in","r",stdin);
    //freopen("d:/data.out","w",stdout);
    int k, i, j;
    scanf("%d", &k);
    for(i = 1; i <= k; i++)
    {
        scanf("%d%d%d", &p[i].h, &p[i].a, &p[i].num);
    }
    sort(p + 1, p + k + 1, cmp);
    memset(f, 0, sizeof(f));
    f[0] = 1;
    int ans = 0;
    for(i = 1; i <= k; i++)
    {
        memset(used, 0, sizeof(used));
        for(j = p[i].h; j <= p[i].a; j++)
        {
            if(!f[j] && f[j - p[i].h] && used[j - p[i].h] + 1 <= p[i].num)
            {
                f[j] = 1;
                used[j] = used[j - p[i].h] + 1;
                ans = max(j, ans);
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}

POJ 1276Cash Machine

裸题不解释

#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <cmath>
#include <ctime>
#define MAXN 10005
#define INF 100000000
#define eps 1e-11
#define L(x) x<<1
#define R(x) x<<1|1
using namespace std;
bool f[100005];
int used[100005];
int main()
{
    //freopen("d:/data.in","r",stdin);
    //freopen("d:/data.out","w",stdout);
    int cash, i, j, n, num[1005], v[1005];
    while(scanf("%d", &cash) != EOF)
    {
        scanf("%d", &n);
        for(i = 1; i <= n; i++)
        scanf("%d%d", &num[i], &v[i]);
        memset(f, 0, sizeof(f));
        f[0] = 1;
        int ans = 0;
        for(i = 1; i <= n; i++)
        {
            memset(used, 0, sizeof(used));
            for(j = v[i]; j <= cash; j++)
            {
                if(!f[j] && f[j - v[i]] && used[j - v[i]] + 1 <= num[i])
                {
                    f[j] = 1;
                    used[j] = used[j - v[i]] + 1;
                    ans = max(ans, j);
                }
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

POJ 3211Washing Clothes

其实就是个0-1背包变形,对同种颜色的衣服,把一个人洗所有衣服的时间算出来,除以二,然后看能达到的最大容量,这就可以保证两人的洗衣时间尽量平均了。也可以用多重背包的做法做,只要把件数都设成1就行。

#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <cmath>
#include <ctime>
#define MAXN 10005
#define INF 100000000
#define eps 1e-11
#define L(x) x<<1
#define R(x) x<<1|1
using namespace std;
bool f[50005];
int v[11][101], cnt[11];
int main()
{
 //freopen("d:/data.in","r",stdin);
 //freopen("d:/data.out","w",stdout);
 int n, m, i, j, x, k;
 char s[22];
 while(scanf("%d%d", &n, &m) != EOF)
 {
   if(n == 0 && m == 0) break;
   map<string, int>mp;
   memset(cnt, 0, sizeof(cnt));
   for(i = 1; i <= n; i++)
   {
     scanf("%s", s);
     string tmp = s;
     mp[tmp] = i;
   }
   for(i = 1; i <= m; i++)
   {
     scanf("%d%s", &x, s);
     string tmp = s;
     cnt[mp[tmp]]++;
     v[mp[tmp]][cnt[mp[tmp]]] = x;
   }
   int ans = 0;
   int mx;
   for(i = 1; i <= n; i++)
   {
     int sum = 0;
     mx = 0;
     for(j = 1; j <= cnt[i]; j++)
       sum += v[i][j];
     for(j = 0; j <= sum / 2; j++)
       f[j] = 0;
     f[0] = 1;
     for(j = 1; j <= cnt[i]; j++)
     {
       for(k = sum / 2; k >= v[i][j]; k--) //倒序的0-1背包做法
       {
         if(!f[k] && f[k - v[i][j]] )
         {
           f[k] = 1;
           mx = max(mx, k);
         }
       }
       /* 多重背包做法
       for(k = 0; k <= sum / 2; k++)
       used[k] = 0;
       for(k = v[i][j]; k <= sum / 2; k++)
       {
         if(!f[k] && f[k - v[i][j]] && !used[k - v[i][j]])
         {
           used[k] = 1;
           f[k] = 1;
           mx = max(mx, k);
         }
       }
       */
     }
     ans += (sum - mx);
   }
   printf("%d\n", ans);
 }
 return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值