背包问题

//01背包 
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int W[30001];
int C[2001];
int R[2001][30001];
int main(){

    int N,M;
    cin>>N>>M;
    for(int i=1;i<=N;i++)
    cin>>C[i]>>W[i];
    for(int i=1;i<=N;i++)
    for(int j=1;j<=M;j++)
    if(j>=C[i])
    {
        R[i][j]=max(R[i-1][j],R[i-1][j-C[i]]+W[i]);
    }
    else
    {
        R[i][j]=R[i-1][j];
    }
    cout<<R[N][M];
    return 0;
}

//完全背包 
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn=300001;
int f[maxn],w[maxn],c[maxn];

int main(){

    ios::sync_with_stdio(false);

    int n,m;

    cin>>m>>n;

    for(int i=1;i<=n;i++) cin>>w[i]>>c[i];

    for(int i=1;i<=n;i++)

       for(int j=w[i];j<=m;j++)

          f[j]=max(f[j],f[j-w[i]]+c[i]);

    cout<<f[m];

    return 0;
}

//多重背包 
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn=300001;
int f[maxn],v[maxn],w[maxn],c[maxn];

int main(){

    ios::sync_with_stdio(false);

    int n,m;

    cin>>n>>m;

    int x,y,z,k=0;

    for(int i=1;i<=n;i++){

        cin>>x>>y>>z;

        int l=1;

        while(z>=l){

            v[++k]=x*l; w[k]=y*l;

            z-=l;//每次减一是处理余数,计算余数大小   l*=2;
        }

        v[++k]=x*z; w[k]=y*z;  //把 n[i]-2*k+1 分类 (处理余数)
    }//其实只用快速将物品分类为对数级,后面操作解决为覆盖到的情况

       假设有1000个苹果,现在要取n个苹果,如何取?正常的做法应该是将苹果一个一个拿出来,直到n个苹果被取出来。

  又假设有1000个苹果和10只箱子,如何快速的取出n个苹果呢?可以在每个箱子中放 2^i (i<=0<=n)个苹果,也就是 1248163264128256489(最后的余数),相当于把十进制的数用二进制来表示,取任意n个苹果时,只要推出几只箱子就可以了。


    for(int i=1;i<=k;i++)   //相当于转化为数量为k的01背包

       for(int j=m;j>=v[i];j--)  //正常的滚动数组操作,之后直接套了 。从小到大更新覆盖所有情况

          f[j]=max(f[j],f[j-v[i]]+w[i]);

    cout<<f[m];

    return 0;
    //没有多重背包题,代码只能存到A+B。。。。。。。。。。。 
}

//混合背包 

g[i]=0为完全背包; g[i]不为0,那g[i]的值为能购买数量

#include <iostream>
#include <algorithm>

using namespace std;

const int maxn=300001;
int f[maxn],w[maxn],c[maxn],g[maxn];

int main(){

    ios::sync_with_stdio(false);

    int n,m;

    cin>>m>>n;

    for(int i=1;i<=n;i++) cin>>w[i]>>c[i]>>g[i];

    for(int i=1;i<=n;i++)

       if(g[i]==0){

             for(int j=w[i];j<=m;j++) 

                f[j]=max(f[j],f[j-w[i]]+c[i]);
       }

       else{

             for(int k=1;k<=g[i];k++)

                for(int j=m;j>=w[i];j--)

                   f[j]=max(f[j],f[j-w[i]]+c[i]);
       }

       cout<<f[m];

       return 0;
}

//多维费用背包 
{
//#include <iostream>
//#include <algorithm>
//
//using namespace std;
//
//const int maxn=2010,maxm=100;
//int f[maxm][maxn][maxn],w[maxn],c[maxn];
//
//int main(){
//    
//    ios::sync_with_stdio(false);
//    
//    int n,v,m;
//    
//    cin>>n>>v>>m;
//    
//    for(int i=1;i<=n;i++) cin>>w[i]>>c[i];
//    
//    for(int i=1;i<=n;i++)
//     
//       for(int j=v;j>=1;j--)
//       
//          for(int z=m;z>=1;z--)
//          
//             if(w[i]<=v and c[i]<=m) f[i][j][z]=max(f[i-1][j][z],f[i-1][j-w[i]][z-c[i]]+1);
//             
//                    else             f[i][j][z]=f[i-1][j][z];
//                    
//    cout<<f[n][v][m];
//    
//    return 0;
//} 
}


#include <iostream>
#include <algorithm>

using namespace std;

const int maxn=3000;
int f[maxn][maxn],w[maxn],c[maxn];

int main(){

    ios::sync_with_stdio(false);

    int n,v,m;

    cin>>n>>v>>m;

    for(int i=1;i<=n;i++) cin>>w[i]>>c[i];

    for(int i=1;i<=n;i++)

       for(int j=v;j>=w[i];j--)     //两次滚动数组操作,增加一维空间,多一层循环 

          for(int z=m;z>=c[i];z--)

             f[j][z]=max(f[j][z],f[j-w[i]][z-c[i]]+1);

    cout<<f[v][m];

    return 0;
}

// 滚动数组来优化果断要ac

分组背包

10 6 3
 2 1 1
 3 3 1
 4 8 2
 6 9 2
 2 8 3
 3 9 3

 for(int i=1;i<=n;i++) cin>>w[i]>>c[i]>>P;

 a[p][++a[p][0]]=i;


      a[1][a[1][0]=1]=1;
      a[1][a[1][0]=2]=2;

      a[2][a[2][0]=1]=3;
      a[2][a[2][0]=2]=4;

      a[3][a[3][0]=1]=5;
      a[3][a[3][0]=2]=6;

      //设a[k][0]为其中一组的个数; 
      //设a[k][i]为其中一组中的成员序数; 

      cin>>m>>n>>t;// n为个数,t为组数;

    for(int i=1;i<=n;i++){

        cin>>w[i]>>c[i]>>p;

        a[p][++z]=i;  //z=0;
    }

    for(int k=1;k<=t;k++)

       for(int j=m;j>=0;j--)

          for(int i=1;i<=a[k][0];i++)

//       for(int i=1;i<=a[k][0];i++)
//       
//          for(int j=m;j>=w[a[k][i]];j--)  如果这样写不保证每组只放一个,可能将多个放入; 

             if(j>=w[a[k][i]]) 

               f[j]=max(f[j],f[j-w[a[k][i]]+c[a[k][i]]);

             cout<<f[m];

             return 0;

//前面推倒完就上代码;

#include <iostream>
#include <algorithm>

using namespace std;

const int maxn=300001,maxm=3000;
int f[maxn],w[maxn],c[maxn];
int a[maxm][maxm];

int main(){

    ios::sync_with_stdio(false);

    int m,n,p,z=0;

    cin>>m>>n>>p;

    for(int i=1;i<=n;i++){

        cin>>w[i]>>c[i]>>p;

        a[p][++a[p][0]]=i;//不用单一变量表示,是因为换组是时需重置,比较麻烦,所以用a[p][0]; 
    }

    for(int k=1;k<=n;k++)

       for(int j=m;j>=0;j--)

          for(int i=1;i<=a[k][i];i++)

             if(j>=w[a[k][i]])

               f[j]=max(f[j],f[j-w[a[k][i]]]+c[a[k][i]]);

    cout<<f[m];

    return 0;
}

背包的方案数

#include <cstdio>
#include <algorithm>
#include <iostream>

using namespace std;

const int maxn=300001;
int f[maxn],a[maxn];

inline int read(){

    int num=0,f=1; char c=getchar();

    while(!isdigit(c)){
        if(c=='-') f=-1; c=getchar();
    }

    while(isdigit(c)){
        num=num*10+c-'0'; c=getchar();
    }

    return num*f;
}

int main(){

    f[0]=1;

    int n,m;

    n=read(); m=read();

    for(int i=1;i<=n;i++) a[i]=read();   

    for(int i=1;i<=n;i++)

       for(int j=m;j>=a[i];j--)

          for(int k=1;k<=j/a[i];k++)//k层可以优化掉,这层并没有什么卵用

             f[j]+=f[j-k*a[i]];//设f[j]为总额为 j的方案数,j-k*a[i]从上一总额推来 

    cout<<f[m];

    return 0;
}

//3 10  
//1
//2
//5
//
//f[10]+=f[9];
//....
//f[10]+=f[0]; // full f[10]=1;
//
//f[9]+=f[8];  // i=1时,i的每次更新都重新更新新的f值 

int main(){

    for(int i=1;i<=n;i++)

       for(int j=a[i];j<=m;j++)
                 f[j]+=f[j-a[i]];//或者设f[j]为面值为j的方案数,从上一面值推来  j-a[i]为上一面值
}

 

转载于:https://www.cnblogs.com/79707536wc/p/7608571.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值