背包问题总结:双线程背包 RQNOJ329,202

来源:互联网 发布:淘宝有一加的旗舰店吗 编辑:程序博客网 时间:2024/05/22 06:13

所谓双线程背包,肯定就是对于一个物品,有两个需要考虑的量,个人对于双线程背包的题目自己还没有遇见比较难的,先放两个正推与逆推的例题好了。

例一:RQNOJ 329 刘翔加油

题目描述

刘翔因伤从北京奥运会的跑道上下来以后,十分的痛苦难过!全国人民看到后都为刘翔加油,支持和关心刘翔!因此,很多人写信来安慰他。没多久,就收到了一大堆信件,可他处理不了这么多,便找到ssxyh处理。ssxyh将信件分了n分,每份信件都有自己的,欣赏价值value,消耗时间time,消耗体力h,和得到的鼓舞w。观看信件必须按照价值递增(大于)的顺序观看,不一定需要全看,例如看了价值45之后就不能再看价值23的了。(为什么?,如果先看了小胡的信当然再看布什的信就不爽了,谁看得下去啊。。。。)可是,翔在伤病中,时间和体力都有限,分别为t,m,同时看完之后体力不能为0(会挂的。。)。这下ssxyh也犯难了,只好请求你帮忙,如何在这些条件下使刘翔获得最大的鼓舞呢??


其实我在此所述的双线程背包也属于01背包的一种变形。

对于题目最简单的状态设计就为dp[i][j][k]表示考虑到前i个,体力值为j,价值为k的最大鼓舞。可能有人会问怎么保证价值递增啊?为什么不sort一下呢?其实放进去的时候虽然不是价值递增的,可是当我们放进去再看的时候可以把它当作价值递增放进去的。当然题目数据似乎保证了不会有价值是一样的存在。

状态转移也很简单对于dp[i][j][k]会由dp[i-1][j-h[i]][k-tim[i]]转移而来,这时我们就发现了,其实这就是一个01背包!所以根据这个状态只会考虑j,k比当前j,k小的状态,所以倒序枚举可以成功压掉一维。

下附AC代码。

#include<iostream>#include<stdio.h>#include<string.h>#include<algorithm>#define maxn 105using namespace std;int n,m,t;struct nod{    int val,tim,h,che;}hum[maxn];int dp[maxn][maxn]; int main(){scanf("%d%d%d",&n,&m,&t);for(int i=1;i<=n;i++)scanf("%d%d%d%d",&hum[i].val,&hum[i].tim,&hum[i].h,&hum[i].che);for(int i=1;i<=n;i++){for(int h=m;h>=hum[i].h+1;h--){for(int ti=t;ti>=hum[i].tim;ti--){dp[h][ti]=max(dp[h][ti],dp[h-hum[i].h][ti-hum[i].tim]+hum[i].che);}}}printf("%d\n",dp[m][t]);} 


例二:RQNOJ 202 奥运火炬登珠峰

题目描述

5月8日,在世界人民的共同关注下,象征着和平、友谊、圣洁的奥运火炬终于来到了世界之巅——珠穆朗玛峰……

登上珠峰可不是所有人都能办得了的,火炬手们为了登山要使用特殊的装备。他有一个带2种气体的气缸:一个为氧气,一个为氮气。让火炬手需要各种的数量的氧和氮。火炬手有一定数量的气缸。每个气缸都有重量和气体容量。火炬手为了完成传递需要特定数量的氧和氮。他完成传递所需气缸的总重的最低限度的是多少?

例如:火炬手有5个气缸。每行三个数字为:氧,氮的(升)量和气缸的重量:

3 36 120

10 25 129

5 50 250

1 45 130

4 20 119

如果火炬手需要5升的氧和60升的氮则总重最小为249 (1,2或者4,5号气缸)。

你的任务就是计算火炬手为了完成传递需要的气缸的重量的最低值。


其实这个题还是有一些很巧妙的地方的。

对于状态的设计,我们使用dp[i][j][k]表示考虑到前i个,氧气是j升,氮气为k升的最小价值。

那么我们如果使用顺推的方法,那么当前的状态就可以转移到dp[i+1][j+O2[i]][k+N2[i]]的这个状态了。最开始我们可能会理所应当将答案认为为dp[n][t][t],可是如果还记着小学的旅行团选车的一类问题,当时的选车可能座位坐不满但是价值更少,这里也可以用同样的思想,如果我们氧气和氮气买多了,如果价值还少那么也无妨。所以我们可以将数组开大,只要大于要求的氧气和氮气量更新答案即可。

下附AC代码。

#include <iostream>#include <stdio.h>#include <string.h>#define maxn 1005#define INF 10000000using namespace std;int o2,n2;int n;int ans=987654321;int dp[maxn][60][170];int v1[maxn],v2[maxn],w[maxn];int main(){    scanf("%d%d%d",&o2,&n2,&n);    for(int i=0;i<n;i++)    {        cin>>v1[i]>>v2[i]>>w[i];    }        memset(dp,-1,sizeof(dp));        dp[0][0][0]=0;        for(int i=0;i<n;i++)    {        for(int j=0;j<=o2;j++)        {            for(int k=0;k<=n2;k++)            {                if(dp[i][j][k]!=-1)                {                    if(dp[i+1][j][k]==-1 || dp[i+1][j][k]>dp[i][j][k])                    {                        dp[i+1][j][k]=dp[i][j][k];                    }                                        if(dp[i+1][j+v1[i]][k+v2[i]]==-1 || dp[i+1][j+v1[i]][k+v2[i]]>dp[i][j][k]+w[i])                    {                        dp[i+1][j+v1[i]][k+v2[i]]=dp[i][j][k]+w[i];                        if(j+v1[i]>=o2 && k+v2[i]>=n2) ans=min(ans,dp[i+1][j+v1[i]][k+v2[i]]);                    }                                        if(j>=o2 && k>=n2) ans=min(ans,dp[i][j][k]);                }            }        }    }    cout<<ans<<endl;}