背包习题

【题目大意】6种价值分别为1 2 3 4 5 6的石头,给出每种石头的数目,问能不能将这些石头分为价值相等的两堆。

【解析】经典的多重背包问题,a[]数组即为1 2 3 4 5 6,count数组题目给出,怎么做呢?上面提到一点,设dp[i]表示容量为i的背包能不能被所给物体装满。如果dp[sum/2]==1,那么就说明可以分为两堆(sum是所有石头的总价值),直接套用上面的模板即可。

#include <cstdio>
#include <cstring>
using namespace std;

int a[10];
int dp[120100];
int main(){
    int cas = 0;
    while( ++ cas ){
        int sum = 0;
        for(int i = 1; i <= 6; ++i){
            scanf("%d",&a[i]);
            sum += a[i]*i;
        }
        if(sum == 0)break;

        memset(dp,0,sizeof(dp));
        printf("Collection #%d:\n",cas);

        if(sum % 2 == 1){
            puts("Can't be divided.");
            puts("");
            continue;
        }

        dp[0] = 1;
        //O(NV)的方法
        int dpt[120000];
        for(int i = 1; i <= 6; ++i){
            memset(dpt,0,sizeof(dpt));
            for(int j = i; j <= sum/2; ++j){
                if (!dp[j] && dp[j-i]&& dpt[j-i] < a[i]) {
                    dpt[j] = dpt[j-i] + 1;
                    dp[j] = 1;
                }
            }
        }
        /*
       //二进制优化的方法
        
       for(int i = 1;i <= 6;++i){
            int k;
            for(k = 1;k*2 < a[i]+1;k *= 2)
            for(int v = sum/2;v >= k*i;--v)
                if(dp[v-k*i])
                    dp[v]=true;
            k = a[i]+1-k;
            for(int v = sum/2;v > k*i;--v)
                if([v-k*i])
                    dp[v] = true;

        }
       */
        if(dp[sum/2]){
            puts("Can be divided.");
        }
        else {puts("Can't be divided.");};
        puts("");

    }
    return 0;
}

【题目大意】给若干组物品,每组物品都有一个箱子(箱子自身也有cost),然后就是物品的cost和value,要买某个物品必须也要买装这个物品的箱子,给一定钱数,问能获得的最大价值。

【题目解析】可以参照背包九讲第六讲和第七讲里的内容解决该问题(相当对口,这题就是根据背包九讲来的)

有一种比较简单的写法,两次背包即可。

#include<iostream>
using namespace std;
const int MAXN = 100000;
int main()
{
    int n,total,boxcost,goodnum,cost,value,i,j,t;
    int dpbox[MAXN],dptotal[MAXN];
    while(scanf("%d%d",&n,&total) != EOF)
    {
        memset(dptotal,0,sizeof(dptotal));
        for(i = 0; i < n; i++)
        {
            scanf("%d%d",&boxcost,&goodnum);
            memcpy(dpbox,dptotal,sizeof(dptotal));
            for(j = 0; j < goodnum; j++)
            {
                scanf("%d%d",&cost,&value);
                for(t = total - boxcost; t >= cost; t--)
                {
                    if(dpbox[t] < dpbox[t - cost] + value)
                    dpbox[t] = dpbox[t-cost] + value;
                }
            }
            for(t = total; t >= boxcost; t--)
            if(dptotal[t] < dpbox[t-boxcost])
            dptotal[t] = dpbox[t-boxcost];
        }
        printf("%d\n",dptotal[total]);
    }
    return 0;
}

【题目大意】一个人有T分钟去做工作,共有n组工作,每一组有若干工作,每个工作消耗的时间和得到的快乐值已知,每一个组有一个标识位,标识位为0,则该组中至少应该选取一样工作完成,标识位为1,则该组中至多有一个工作被选择,标识位为2,无限制。

【分析】对标识位为2的工作组,因为无限制条件,每个工作可做可不做,即用简单的01背包即可处理。对标识位为1的工作组,因为至多选择一个工作,所以本次操作只对dp数组选取最优值更新一次即可。对至少选择一个的工作组,还不是太理解,只是看别人代码写的,还请大家集思广益,有想法可以留言评论,以后理解透彻了以后会做补充。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <map>

using namespace std;

const int maxt = 100 + 10;

int dp[maxt];
int td[maxt], n, m, t;

void dp0() {   //至少取一个
    memcpy (td, dp, sizeof(td));
    memset (dp, -1, sizeof(dp));
    for (int i = 0, a, b; i < m; ++i) {
        scanf ("%d%d", &a, &b);
        for (int j = t; j >= a; --j) {//注意怎样保证至少取一个??
            if (dp[j-a] != -1) {
                dp[j] = max(dp[j], dp[j-a] + b);
            }
            if (td[j-a] != -1) {
                dp[j] = max(dp[j], td[j-a] + b);
            }
        }
    }
}

void dp1() {   //最多取一个,用上次得到的dp值更新最优值,只更新一次
    memcpy(td, dp, sizeof(td));
    for (int i = 0, a, b; i < m; ++i) {
        scanf ("%d%d", &a, &b);
        for (int j = t; j >= a; --j) {
            if (td[j-a] != -1) {
                dp[j] = max(dp[j], td[j-a] + b);
            }
        }
    }
}

void dp2() {  //无条件限制,01背包,更新多次
    for (int i = 0, a, b; i < m; ++i) {
        scanf ("%d%d", &a, &b);
        for (int j = t; j >= a; --j) {
            if (dp[j-a] != -1) {
                dp[j] = max(dp[j], dp[j-a] + b);
            }
        }
    }
}

void init() {
    memset (dp, -1, sizeof(dp));
    dp[0] = 0;
    for (int i = 0, s; i < n; ++i) {
        scanf ("%d%d", &m, &s);
        if (s == 0) dp0();
        else if (s == 1) dp1();
        else if (s == 2) dp2();
    }
}

void solve() {
    int ans = -1;
    for (int i = 0; i <= t; ++i)
        ans = max(ans, dp[i]);
    printf ("%d\n", ans);
}

int main() {
    //freopen("ar.txt","r",stdin);
    while (scanf ("%d%d", &n, &t) != EOF) {
        init();
        solve();
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值