基础背包---01;完全;01背包之2;多重(二进制优化);二维费用流;分组

1.01背包:

选或不选这件

记dp[i+1][j]为从0到i这i+1个物品中挑选 总重小于j时,总价值的最大值

dp[i+1][j]=dp[i][j](j<w[i]时)

dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i])(其他)

int dp[MAX_N+1][MAX_M+1];
int v[i],w[i];
int W//总重量不超过W
void solve(){
    for(int i=0;i<n;i++){
        for(int j=0;j<=W;j++){
            if(j<w[i])dp[i+1][j]=dp[i][j];
            else dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]);
        }
    }
    printf("%d\n",dp[n][W]);
}
View Code

 

2.完全背包:

一件东西有无限件,可以取无限次

记dp[i+1][j]为从0到i这i+1个物品中挑选 总重小于j时,总价值的最大值

如果和01背包类似的话,就有三重循环,复杂度过高

但是发现在dp[i+1][j]的计算中选择k(k>=1)个的情况,和在dp[i+1][j-w[i]]的计算中选择k-1个的情况时一样的

所以:

 

 

dp[i+1][j]=dp[i][j](j<w[i]时)

dp[i+1][j]=max(dp[i][j],dp[i+1][j-w[i]]+v[i])(其他)

int dp[MAX_N+1][MAX_M+1];
int v[i],w[i];
int W//总重量不超过W
void solve(){
    for(int i=0;i<n;i++){
        for(int j=0;j<=W;j++){
            if(j<w[i])dp[i+1][j]=dp[i][j];
            else dp[i+1][j]=max(dp[i][j],dp[i+1][j-w[i]]+v[i]);
        }
    }
    printf("%d\n",dp[n][W]);
}
View Code
01int n,i,j,m,w[35],v[35],dp[205];// 最多30个物品,最大容量m=200
      cin>>m>>n;
      for(i=1;i<=n;i++)cin>>w[i]>>v[i];//重量,价值 
      for(i=1;i<=n;i++)
            for(j=m;j>=w[i];j--)//逆序 
                   dp[j]=max(dp[j-w[i]]+v[i],dp[j]);
      cout<<dp[m]<<endl;
完全:
 int n,m,i,j,w[32],c[32],dp[202]; 
      scanf("%d%d",&m,&n);//容量,种数 
      for(i=1;i<=n;i++)cin>>w[i]>>v[i];//重量,价值 
      for(i=1;i<=n;i++)
                for(j=w[i];j<=m;j++)
                    dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
      printf("max=%d",dp[m]);
一维的01和完全背包

 

3.01背包之2

背景和01背包一样,都是计算不超过W重量的最大价值

但是条件变了:

1<=n<=100

1<=wi<=10^7

1<=vi<=100

1<=W<=10^9

之前01背包的复杂度是O(nW),所以之前的方法肯定不行

现在W太大,反而vi小了,所以从vi入手

记dp[i+1][j]为从0到i这i+1个物品中挑选 价值为 j 时,总重量的最小值(不存在就是INF)

所以dp[0][0]=0,dp[0][j]=INF

dp[i+1][j]=dp[i][j](j<v[i]时)

dp[i+1][j]=max(dp[i][j],dp[i][j-v[i]]+w[i])(其他)

int dp[MAX_N+1][MAX_N*MAX_V+1];
int v[i],w[i];
int W;//最大重量
void solve(){
    for(int i=0;i<n;i++){
        for(int j=0;j<=MAX_N*MAX_V;j++){
            if(j<v[i])dp[i+1][j]=dp[i][j];
            else dp[i+1][j]=max(dp[i][j],dp[i][j-v[i]]+w[i]);
        }
    }
    inr res=0;
    for(int i=0;i<=MAX_N*MAX_V;i++)if(dp[n][i]<=W)res=i;
    printf("%d\n",res);
}
View Code

 

技巧:如果要求恰好装满背包,那么初始化dp[0][0]=0;dp[0][j]=-INF(因为要求恰好装满:只有容量为j=0时在啥也不装的情况下有0这个解,容量为j(j>0)时则无解)

若没有要求必须装满,只是希望价格尽量大,初始化就应全设为0;(因为不要求装满,则可以假设啥也不装,那么解就可以都为0)

4.多重背包:

多重背包问题的思路跟完全背包的思路非常类似,只是k的取值是有限制的,因为每件物品的数量是有限制的,状态转移方程为:

    dp[i+1][j] = max{dp[i][j - k * w[i]] + v[i] | 0 <=k <= n[i]}  

当W<=n[i]*w[i]时,可以无限取(总重不超过W)同完全背包

当W>n[i]*w[i]时,同01背包;将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为1,2,4,...,2^(k-1),n[i]-2^k+1;使得选择该物品的最佳个数都可由这些系数相加得来

例题:tzoj5736

#include<bits/stdc++.h>
using namespace std;
int m[2005],w[2005],s[2005];//个数 重量 价值
int n,W,dp[5005];
void zeroone(int w,int v)
{
    int i;
    for(i=W; i>=w; i--)
        dp[i]=max(dp[i],dp[i-w]+v);
}
void com(int w,int v)//完全
{
    for(int i=w;i<=W;i++)dp[i]=max(dp[i],dp[i-w]+v);
}
void multidp(int w,int v,int cnt)
{
    if(cnt*w>=W)
    {
        com(w,v);
        return;
    }
    int k=1;
    while(k<=cnt)//二进制优化
    {
        zeroone(k*w,k*v);
        cnt-=k;
        k*=2;
    }
    zeroone(cnt*w,cnt*v);
    return ;
}
int main()
{
    int i,j,k;
    scanf("%d%d",&n,&W);//物品数,最大体重
    for(i=0;i<n;i++)scanf("%d%d%d",&m[i],&w[i],&s[i]);
    for(i=0;i<n;i++)multidp(w[i],s[i],m[i]);
    printf("%d\n",dp[W]);
}

5736
5736

 

5.二维费用流背包:

在一维基础上再加一维,其他和上面的背包一样

例题:(toj 5742)(01背包加一维)

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
int dp[1005][25][85];
int n,m,k,a[1005],b[1005],w[1005];
int main()
{
    int i,j,p;
    scanf("%d%d",&m,&n);
    scanf("%d",&k);
    for(i=0;i<k;i++)scanf("%d%d%d",&a[i],&b[i],&w[i]);
    for(i=0;i<=m;i++){
        for(j=0;j<=n;j++)dp[0][i][j]=INF;
    }
    dp[0][0][0]=0;
    for(i=0;i<k;i++){
        for(j=0;j<=m;j++){
            for(p=0;p<=n;p++){
                int x=j-a[i],y=p-b[i];
                int rec;//记录选这件 要付出的重量
                if(x<0&&y>=0)rec=dp[i][0][y]+w[i];
                else if(x>=0&&y<0)rec=dp[i][x][0]+w[i];
                else if(x<0&&y<0)rec=dp[i][0][0]+w[i];
                else if(x>=0&&y>=0)rec=dp[i][x][y]+w[i];
                dp[i+1][j][p]=min(dp[i][j][p],rec);
            }
        }
    }
    printf("%d\n",dp[k][m][n]);
 }
5742

 

6.分组背包:

最外层循环每组;中间和01背包一样循环W,最内层循环每个组的物品--保证每组只选一个或不选

例题:tzoj:5743

#include<bits/stdc++.h>
using namespace std;
int n,V,t,w[31],c[31],dp[202];
vector<int>v[12];
int main()
{
    int i,j,k;scanf("%d%d%d",&V,&n,&t);
    for(i=0;i<n;i++){
        int p;
        scanf("%d%d%d",&w[i],&c[i],&p);
        v[p].push_back(i);
    }
    for(i=1;i<=t;i++){
        for(j=V;j>=0;j--){
            for(k=0;k<v[i].size();k++){
                int u=v[i][k];
                if(j>=w[u])dp[j]=max(dp[j],dp[j-w[u]]+c[u]);//注意if判断
            }
        }
    }
    printf("%d\n",dp[V]);
}
/*
10 1 3
2 1 1
*/
View Code

 

posted @ 2020-05-25 10:12  -第4题-  阅读(266)  评论(0编辑  收藏  举报