背包习题

来源:互联网 发布:程序员加薪申请书范文 编辑:程序博客网 时间:2024/05/20 11:52

【题目大意】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;}


原创粉丝点击