基础背包(一)

hdu 2602 01背包

许多年前,在泰迪的家乡,有一个人被称为“骨收藏家”。这个人喜欢收集各种各样的骨头,比如狗的,牛的,他也去了坟墓

采骨者有一个体积为v的大袋子,在他的采集过程中有很多骨头,很明显,不同的骨头有不同的价值和体积,现在考虑到每一块骨头的价值,你能计算出采骨者能得到的总价值的最大值吗

输入

第一行包含整数t,即事例数。

接下来是t例,每例三行,第一行包含两个整数n,v,(n<=1000,v<=1000),表示骨骼数量和他的包的体积。第二行包含n个整数,表示每个骨骼的值。第三行包含n个整数,表示每个骨骼的体积。

产量 

每行一个整数,表示总值的最大值(此数字将小于2 31)。

解题思路:简单01背包

一维解法:

#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=1100;
int n,V,dp[maxn],weight[maxn],value[maxn],num[maxn];
int main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n>>V;
        for(int i=1;i<=n;i++)cin>>value[i];
        for(int i=1;i<=n;i++)cin>>weight[i];
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++){
            for(int j=V;j>=weight[i];j--)
                dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
        }
        cout<<dp[V]<<endl;
    }
    return 0;
}

二维解法:

#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=1100;
int n,V,dp[maxn][maxn],weight[maxn],value[maxn],num[maxn];

int main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n>>V;
        for(int i=1;i<=n;i++)cin>>value[i];
        for(int i=1;i<=n;i++)cin>>weight[i];
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++){
            for(int j=0;j<=V;j++){
                if(j>=weight[i])dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
                else dp[i][j]=dp[i-1][j];
            }
        }
        cout<<dp[n][V]<<endl;
    }
    return 0;
}

 

hdu 1114  完全背包

题目大意:

输入空的存钱罐的重量以及满了的重量
输入硬币种类数t
以下t行输入t种硬币每个硬币的重量以及面值
求此情况下该存钱罐至少能存的钱数,无解就输出无解
 
解题思路:完全背包问题,注意初始化,初始化为无穷大表示不合法,如果计算最后还是无穷大则表示无解,把max改成min就好了
一维解法:
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=11000;
int n,V,dp[maxn],weight[maxn],value[maxn],num[maxn];

int main(){
    int t;
    cin>>t;
    while(t--){
        int v1,v2;
        cin>>v1>>v2;
        V=v2-v1;
        cin>>n;
        for(int i=1;i<=n;i++)cin>>value[i]>>weight[i];
        memset(dp,0x3f,sizeof(dp));
        dp[0]=0;
        for(int i=1;i<=n;i++)
            for(int j=weight[i];j<=V;j++)
                dp[j]=min(dp[j],dp[j-weight[i]]+value[i]);
        if(dp[V]==0x3f3f3f3f)puts("This is impossible.");
        else printf("The minimum amount of money in the piggy-bank is %d.\n",dp[V]);
    }
    return 0;
}

二维解法:

#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=11000;
int n,V,dp[505][maxn],weight[maxn],value[maxn],num[maxn];

int main(){
    int t;
    cin>>t;
    while(t--){
        int v1,v2;
        cin>>v1>>v2;
        V=v2-v1;
        cin>>n;
        for(int i=1;i<=n;i++)cin>>value[i]>>weight[i];
        memset(dp,0x3f,sizeof(dp));
        for(int i=0;i<=n;i++)dp[i][0]=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=V;j++){
                if(j>=weight[i]) dp[i][j]=min(dp[i-1][j],dp[i][j-weight[i]]+value[i]);
                else dp[i][j]=dp[i-1][j];
            }
        }
        if(dp[n][V]==0x3f3f3f3f)puts("This is impossible.");
        else printf("The minimum amount of money in the piggy-bank is %d.\n",dp[n][V]);
    }
    return 0;
}

 

hdu 1171 多重背包

题目大意:给你n个物品的价值和物品的数量,如何分使得A,B所得价值最接近并且A的价值不能小于B

解题思路:完全背包,背包的容量为价值的总和的一半

#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn=110000;
int n,V,dp[55][maxn],weight[maxn],value[maxn],num[maxn];
int main(){
    while(cin>>n){
        if(n<0)break;
        int sum=0;
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++){
            cin>>value[i]>>num[i];
            weight[i]=value[i];
            sum+=value[i]*num[i];
        }
        V=sum/2;
        if(sum%2)V++;
        for(int i=1;i<=n;i++)
            for(int j=0;j<=num[i];j++)
                for(int k=V;k>=j*weight[i];k--)
                    dp[i][k]=max(dp[i-1][k],dp[i-1][k-j*weight[i]]+j*value[i]);
        cout<<max(dp[n][V],sum-dp[n][V])<<" "<<min(dp[n][V],sum-dp[n][V])<<endl;
    }
    return 0;
}

 

hdu 2844 多重背包+二进制优化

题目大意:给出n种硬币的价值以及数目,求这n种硬币可以在1-m中可以支付的价格有多少种

解题思路:硬币的价值同时也是硬币的代价,dp[j]表示容量为j的背包所可以得到的最大价值,如果等于j表示可以支付价钱j

#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn=110000;
int n,m,V,dp[maxn],value[maxn],num[maxn],vis[maxn],weight[maxn];
int main(){
    while(cin>>n>>m){
        if(n==0&&m==0)break;
        for(int i=1;i<=n;i++) cin>>value[i];
        for(int i=1;i<=n;i++) cin>>num[i];
        int ans=0;
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++){
            if(value[i]*num[i]>m){
                for(int j=value[i];j<=m;j++)  //完全背包 
                    dp[j]=max(dp[j],dp[j-value[i]]+value[i]);
            }else{
                //二进制优化01背包 
                for(int j=1;j<=num[i];j*=2){
                    for(int k=m;k>=j*value[i];k--)
                        dp[k]=max(dp[k],dp[k-j*value[i]]+j*value[i]);
                    num[i]-=j;
                }
                if(num[i]){
                    for(int k=m;k>=num[i]*value[i];k--)
                        dp[k]=max(dp[k],dp[k-num[i]*value[i]]+num[i]*value[i]);
                }
            }
        }
        for(int i=1;i<=m;i++)
        if(dp[i]==i)ans++;
        cout<<ans<<endl;        
    }
    return 0;
}

 

hdu 1059 多重背包+二进制优化

题目大意:给你6种不同价值的硬币的数量,价值分别是1~6,问你能否可以将这些硬币分成两堆,每堆的价值一样。

解题思路:和hdu1171很像,把价值总和的一半看成背包容量,计算形成最大的价值是否可以达到价值总和的一半

#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn=110000;
int n,V,dp[maxn],value[maxn],num[maxn],vis[maxn],weight[maxn];
int main(){
    int T=0;
    while(cin>>num[1]>>num[2]>>num[3]>>num[4]>>num[5]>>num[6]){
        int sum=0;
        n=6;
        T++;
        for(int i=1;i<=6;i++)sum+=num[i];
        if(sum==0)break;
        sum=0;
        for(int i=1;i<=6;i++){
            value[i]=i;
            sum+=value[i]*num[i];
        }
        printf("Collection #%d:\n",T);
        if(sum%2){
            puts("Can't be divided.");
            puts("");
            continue;
        }
        V=sum/2;
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++){
            if(value[i]*num[i]>V){
                for(int j=value[i];j<=V;j++)  //完全背包 
                    dp[j]=max(dp[j],dp[j-value[i]]+value[i]);
            }else{
                //二进制优化 
                for(int j=1;j<=num[i];j*=2){
                    for(int k=V;k>=j*value[i];k--)
                        dp[k]=max(dp[k],dp[k-j*value[i]]+j*value[i]);
                    num[i]-=j;
                }
                if(num[i]){
                    for(int k=V;k>=num[i]*value[i];k--)
                        dp[k]=max(dp[k],dp[k-num[i]*value[i]]+num[i]*value[i]);
                }
            }
        }
        if(dp[V]==V) puts("Can be divided.");
        else puts("Can't be divided.");
        puts("");
    }
    return 0;
}

 

posted @ 2019-04-03 20:50  两点够吗  阅读(270)  评论(0编辑  收藏  举报