zoukankan      html  css  js  c++  java
  • 01背包 多重背包 复习 模板

    HDU 2126  01背包方案计数

    http://acm.hdu.edu.cn/showproblem.php?pid=2126

    题意   n个纪念品 m块钱 问m块钱最多能买几个纪念品并输出方案数。

    解析   01背包的方案数我们可以统计 但是要知道物品数量 所以要多开一维记录信息

              dp[ i ][ k ][ j ]表示 前i个物品 花 j 元钱 买k个物品的方案数

              dp[ i ][ k ][ j ] = dp[ i-1 ][ k ][ j ] + dp[ i-1][ k-1 ][ j-v[i] ];

    AC代码

    #include <bits/stdc++.h>
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define all(a) (a).begin(), (a).end()
    #define fillchar(a, x) memset(a, x, sizeof(a))
    #define huan printf("
    ")
    #define debug(a,b) cout<<a<<" "<<b<<" "<<endl
    #define ffread(a) fastIO::read(a)
    using namespace std;
    typedef long long ll;
    const int maxn = 6e3+10;
    const int inf = 0x3f3f3f3f;
    const ll mod = 1000000009;
    const double epx = 1e-6;
    const double pi = acos(-1.0);
    //head------------------------------------------------------------------
    ll dp[35][35][505],v[35];
    int main()
    {
        int t,n,m;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&n,&m);
            fillchar(dp,0);
            for(int i=1;i<=n;i++)
                scanf("%lld",&v[i]);
            for(int i=0;i<=m;i++)
                for(int j=0;j<=n;j++)
                    dp[j][0][i]=1;
            for(int i=1;i<=n;i++)
            {
                for(int k=1;k<=i;k++)
                {
                    for(int j=1;j<=m;j++)
                    {
                        if(j>=v[i])
                            dp[i][k][j]=dp[i-1][k][j]+dp[i-1][k-1][j-v[i]];
                        else
                            dp[i][k][j]=dp[i-1][k][j];
                    }
                }
    //            for(int j=1;j<=m;j++)
    //            {
    //                for(int k=1;k<=i;k++)
    //                    cout<<k<<" "<<j<<" "<<dp[i][k][j]<<" ";
    //                cout<<endl;
    //            }
            }
            int flag=0;
            for(int i=n;i>=1;i--)
            {
                if(dp[n][i][m]>0)
                {
                    printf("You have %lld selection(s) to buy with %d kind(s) of souvenirs.
    ",dp[n][i][m],i);
                    flag=1;
                    break;
                }
            }
            if(!flag)
                printf("Sorry, you can't buy anything.
    ");
        }
    }

    HDU 2079  

    http://acm.hdu.edu.cn/showproblem.php?pid=2079

    题意 n个学分 k种课 第 i 种课的学分为ai 数量为bi  问刚好修满n个学分的选课方案数(相同种类的课程没有区别)

    解析 这道题可以用母函数写。但是我们考虑多重背包怎么写,很容易想到转换成01背包,但是不作处理直接一个一个枚举会有计数重复的情况。

    考虑选修第 i 类课的话 我们直接枚举数量 从大到小更新 从而避免重复计数 

    dp[ k ] += dp[ k-j*v[ i ] ] 不可以dp[ k ] += dp[ k-j*v ] 因为求的是方案数不是最大值,不然会有重复。

    AC代码

    #include <bits/stdc++.h>
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define all(a) (a).begin(), (a).end()
    #define fillchar(a, x) memset(a, x, sizeof(a))
    #define huan printf("
    ")
    #define debug(a,b) cout<<a<<" "<<b<<" "<<endl
    #define ffread(a) fastIO::read(a)
    using namespace std;
    typedef long long ll;
    const int maxn = 6e3+10;
    const int inf = 0x3f3f3f3f;
    const ll mod = 1000000009;
    const double epx = 1e-6;
    const double pi = acos(-1.0);
    //head------------------------------------------------------------------
    int v[10],num[10],dp[50];
    int main()
    {
        int t,n,m;
        scanf("%d",&t);
        while(t--)
        {
            fillchar(dp,0);
            scanf("%d%d",&m,&n);
            for(int i=1;i<=n;i++)
                scanf("%d%d",&v[i],&num[i]);
            dp[0]=1;
            for(int i=1;i<=n;i++)
            {
                for(int k=m;k>=v[i];k--)
                {
                    for(int j=1;j<=num[i];j++)
                    {
                        if(k>=j*v[i])            
                           dp[k]+=dp[k-j*v[i]];
                    }
                }
            }
            printf("%d
    ",dp[m]);
        }
    }

    HDU2191

     http://acm.hdu.edu.cn/showproblem.php?pid=2191

    题意 多重背包求最大值 裸题 

    解析  可以转化成01背包O( v*m*sum_num[i] )  二进制优化O( v*m*sum_log( num[i] ) ) 单调队列O( v*m )

    二进制

    #include <bits/stdc++.h>
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define all(a) (a).begin(), (a).end()
    #define fillchar(a, x) memset(a, x, sizeof(a))
    #define huan printf("
    ")
    #define debug(a,b) cout<<a<<" "<<b<<" "<<endl
    #define ffread(a) fastIO::read(a)
    using namespace std;
    typedef long long ll;
    const int maxn = 6e3+10;
    const int inf = 0x3f3f3f3f;
    const ll mod = 1000000009;
    const double epx = 1e-6;
    const double pi = acos(-1.0);
    //head------------------------------------------------------------------
    int n,m;  //n为物品的种类数,m为背包最大承载重量
    int dp[1005];    // dp状态数组
    int num[1005];   //物品的数量
    int value[1005]; //物品的价值
    int weight[1005];//物品的重量
    /*
    多重背包问题的二进制解法
    */
    void zeroonebag(int heft,int worth)  //01背包
    {   //heft为当前物品的重量,worth为当前物品的价值
        for(int i=m;i>=heft;i--)
            dp[i]=max(dp[i],dp[i-heft]+worth);
    }
    void completeBag(int heft,int worth)  //完全背包
    {   //heft为当前物品的重量,worth为当前物品的价值
        for(int i=heft;i<=m;i++)
            dp[i]=max(dp[i],dp[i-heft]+worth);
    }
    int main()
    {
        int t;
        cin>>t;
        while(t--)
        {
            memset(dp,0,sizeof(dp));  //初始化dp数组 只求最大值
            //memset(dp,-inf,sizeof(dp));dp[0]=0; 要求恰好装满求最大值 最小值设为inf
            cin>>m>>n;   //n物品种类 m总容量
            for(int i=0;i<n;i++)
                cin>>weight[i]>>value[i]>>num[i];  //输入物品的重量 输入物品的价值 输入物品的个数
            for(int i=0;i<n;i++)  //遍历每一类物品
            {
                if(weight[i]*num[i]>=m)
                {   //如果同一类物品的总重量大于背包的最大容量,则转换成完全背包
                    completeBag(weight[i],value[i]);
                }
                else  //多个01背包
                {
                    for(int t=1;t<=num[i];t*=2)  //二进制的运用
                    {
                        zeroonebag(weight[i]*t,value[i]*t);
                        num[i]-=t;
                    }
                    zeroonebag(weight[i]*num[i],value[i]*num[i]);
                }
            }
            cout<<dp[m]<<endl;  //输出结果
        }
        return 0;
    }

    单调队列 并未获得该技能包。。。QAQ  技能包

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<climits>
    #define ll long long
    using namespace std;
    int n,m,he,ta,T;
    ll f[10010],q[10010],num[10010];
    int main()
    {
        int i,j,w,v,s,d;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&m,&n);
            for(i=0; i<=m; i++)
                f[i]=0;
            for(i=1; i<=n; i++)
            {
                scanf("%d%d%d",&w,&v,&s);
                if(s>m/w)
                    s=m/w;
                for(d=0; d<w; d++)
                {
                    he=ta=1;
                    for(j=0; j<=(m-d)/w; j++) //先存进去,后取出来
                    {
                        int tmp=f[j*w+d]-v*j;
                        while(he<ta&&q[ta-1]<=tmp)
                            --ta;
                        q[ta]=tmp,num[ta++]=j;
                        while(he<ta&&j-num[he]>s)
                            ++he;
                        f[j*w+d]=max(f[j*w+d],q[he]+v*j);
                    }
                }
            }
            printf("%lld
    ",f[m]);
        }
        return 0;
    }
  • 相关阅读:
    半平面交模板
    poj2420(模拟退火大法好)
    hdu4266(三维凸包模板题)
    三维凸包模板
    三维空间直线最近点对hdu4741
    3维计算几何模板
    hdu1174(3维射线与圆是否相交)
    重点记忆
    UNICODE,GBK,UTF-8区别
    AJAX 基础
  • 原文地址:https://www.cnblogs.com/stranger-/p/10505870.html
Copyright © 2011-2022 走看看