zoukankan      html  css  js  c++  java
  • 一本通1269 有限背包

    【题目描述】

    为了庆贺班级在校运动会上取得全校第一名成绩,班主任决定开一场庆功会,为此拨款购买奖品犒劳运动员。期望拨款金额能购买最大价值的奖品,可以补充他们的精力和体力。

    【输入】

    第一行二个数n(n≤500),m(m≤6000),其中n代表希望购买的奖品的种数,m表示拨款金额。

    接下来n行,每行3个数,v、w、s,分别表示第I种奖品的价格、价值(价格与价值是不同的概念)和能购买的最大数量(买0件到s件均可),其中v≤100,w≤1000,s≤10。

    【输出】

    一行:一个数,表示此次购买能获得的最大的价值(注意!不是价格)。

    【输入样例】

    5 1000
    80 20 4
    40 50 9
    30 50 7
    40 30 6
    20 20 1
    

    【输出样例】

    1040

    思路:其实这个题完全可以改一下完全背包的代码,只要把循环里的条件改成几件就可以,但是那样的话复杂度是O(n^3),有点慢,所以改进一下代码。

    这个拆分的思路非常巧妙(也有可能我是第一次听所以感觉巧妙),把一个数拆成2的n次方和一个别的数,比如说33,33=1+32=1+2+30=1+2+4+26=1+2+4+8+18=1+2+4+8+16+2,把这个数拆开,然后把其中的任意几个数相加,会发现,这些数的和可以取到1~33中的任意值,这样就可以大大简化时间复杂度,先上代码

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<bits/stdc++.h>
    using namespace std;
    int f[6500],v[510],w[510],k[510],v_[6500],w_[6500];//数组大小开到n*log2(m)就可以
    int n,m,cnt;
    int main()
    {
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    cin>>v[i]>>w[i]>>k[i];//输入
    for(int i=1;i<=n;i++)
    {
    int r=1;//2的0次方开始
    while(r<=k[i])//如果可以再拆,那么继续
    {
    cnt++;//cnt这个变量的含义其实可以理解为种类,比如说把两个第一类物品捆绑到一起,就变成了一种物品,因为这种物品可以通过和其他的加来得到任意一个数,所以这样做是没问题的
    v_[cnt]=v[i]*r;//新数组,就是捆绑之后的数据
    w_[cnt]=w[i]*r;
    k[i]-=r;//别忘了-r
    r=r*2;//2的下一次方
    }
    if(k[i]!=0)//如果此时还有剩余,那么再开一个,存进去
    {
    cnt++;
    v_[cnt]=v[i]*k[i];
    w_[cnt]=w[i]*k[i];
    k[i]=0;
    }
    }
    for(int i=1;i<=cnt;i++)//循环,从第一种开始循环
    for(int j=m;j>=v_[i];j--)//其实下面就是01背包的操作
    f[j]=max(f[j],f[j-v_[i]]+w_[i]);
    cout<<f[m];
    return 0;
    }

  • 相关阅读:
    一本通1018
    并查集&MST
    hdu 1875 畅通工程再续
    hdu 1811 Rank of Tetris(拓扑排序+并查集)
    hdu 1325 is it a tree?
    hdu1285拓扑排序
    hdu2063 过山车(最大二分匹配)
    最小生成树二·Kruscal算法
    hiho一下 第二十一周(线段树 离散化)
    hiho一下 第二十周(线段树模板)
  • 原文地址:https://www.cnblogs.com/57xmz/p/12735049.html
Copyright © 2011-2022 走看看