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("\n")
#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.\n",dp[n][i][m],i);
                flag=1;
                break;
            }
        }
        if(!flag)
            printf("Sorry, you can't buy anything.\n");
    }
}

 

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("\n")
#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\n",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("\n")
#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\n",f[m]);
    }
    return 0;
}

 

posted @ 2019-03-10 16:50  灬从此以后灬  阅读(165)  评论(0编辑  收藏  举报