HDU 2191 珍惜现在,感恩生活。将多重背包经二进制划分变为01背包。

来源:互联网 发布:杨辉三角形 java 编辑:程序博客网 时间:2024/06/07 07:14

多重背包问题,不同种类的大米有不同的袋数,要求在资金有限的情况下获得重量最大值。
直接求解有些困难,我们可以换个角度思考:
针对某种 p元/h千克 共c袋的大米(我们可以给它们标记为第M0、M1、、、Mc袋大米),它可能出现(2的c次方)种情况,即对每一袋独立的大米Mi (i=0~c)来说,它可能取0也可能取1(取或不取)。这不正是01背包的思想吗?哪要管袋数的限制,统统拆分为独立的一袋统一进行01背包式求解。
我知道你已经灵光一现准备代码实现了。But wait,让我们分析一下时间复杂度:
O[((i=0~m)Σi(第i种大米的袋数))x(从n到0遍历)]=O(n^3)

嗯…n立方的复杂度,有点不能接受。能不能优化一下呢?答案当然是可以的:
我们注意到如果一件一件的拆分会存在大量重复计算。如对于 4袋1元/千克的大米 不论我们是选0001、0010、0100还是1000本质上是一样的都只是选了一袋。由0和1我们联想到二进制,而二进制数的表示是唯一的,我们为什么不干脆用二进制的方法划分大米呢?这样取x袋的组合数几乎是惟一的(因为袋数不可能完全符合2的n次方,多出部分可能构成与之前表示相同的结果),将大大精简运算。
如对于10袋相同大米,我们按 1、2、4、3划分为四部分,则取2袋的代码则为0100、取3袋为1100、4袋为0010等等。
由此我们大米袋数将减少至logn,复杂度优化为O(n*n*logn)

好了,我们来看一下代码实现吧:

#include <iostream>#include <cstdio>#include <cstdlib>#include <memory.h>#include <cstring>#include <queue>#include <cmath>#include <algorithm>#define IN freopen("in.txt", "r", stdin)#define OUT freopen("out.txt", "wb", stdout)#define max(a, b)  (((a) > (b)) ? (a) : (b))#define min(a, b)  (((a) < (b)) ? (a) : (b))#define N 110using namespace std;/**    采取二进制拆解,复杂度为O((n*logn)*n)即 种类*价格*/struct node{    int price;    int weight;};node rice[N*7];     //由于一种大米最多有100袋,则最多拆解为log2(100)=7次,即100种大米最多拆解为 100*7=700 袋大米int ri,n,m,p,h,c;   //ri用于记录已经记录了多少种大米了、n:经费、m:大米种类、p:单价、h:重量、c:对应袋数int weight[N];      //weight[i]表示在 i元经费的情况下所能获得的最大重量//函数功能:对该种类大米进行二分拆解void binary(){    int temp,found;    for(found=1;c>0;found*=2)    {        c-=found;        if(c<0) //剩余袋数不满足2的n次方了            found+=c;        rice[ri].price=p*found;        rice[ri].weight=h*found;        ri++;    }}int main(){    int t,i,j,con;  //con用于记录最终结果    cin>>t;    while(t-->0)    {        cin>>n>>m;        memset(weight,0,sizeof(int)*(n+1));        ri=con=0;   //设置初值        while(m-->0)        {   //读入大米信息并二分划分            cin>>p>>h>>c;            binary();        }        for(i=0;i<ri;i++)            for(j=n;j>=0;j--)                if(j-rice[i].price>=0 && weight[j] < weight[j-rice[i].price]+rice[i].weight )                {   //状态转移方程:weight[j] = max(weight[j], weight[j-rice[i].price]+rice[i].weight );                    weight[j] = weight[j-rice[i].price]+rice[i].weight;                    if(weight[j]>con)                        con=weight[j];                }        cout<<con<<endl;    }    return 0;}
0 0
原创粉丝点击