01背包问题总结

总结一下 按照:http://blog.csdn.net/libin56842/article/details/9338841 这个博客 提供的题号(感谢大牛) 和自己多加的几道01背包的题目。

其状态转移方程是:

f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}

01背包的裸代码:(节省一维空间)

for(int i=0;i<n;i++)
 for(int j=V;j>=weight[i];j--)
   dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);

1.HDU2602:Bone Collector
01背包裸题
http://acm.hdu.edu.cn/showproblem.php?pid=2602

#include<cstring>
#include<climits>
#include<cstdio>
#include<cstdlib>
#include<map>
#include<stack>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1010;
int dp[maxn],v[maxn],w[maxn];
int main()
{
    int T;cin>>T;
    while(T--){
        memset(dp,0,sizeof(dp));
        int n,W;cin>>n>>W;
        for(int i=0;i<n;i++) cin>>v[i];
        for(int i=0;i<n;i++) cin>>w[i];
        for(int i=0;i<n;i++)
           for(int j=W;j>=w[i];j--)
           dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
        cout<<dp[W]<<endl;
    }
}

2.hdu2546 :饭卡
http://acm.hdu.edu.cn/showproblem.php?pid=2546
很经典的一道01背包题,要注意的是这里只要剩余的钱不低于5元,就可以购买任何一件物品,所以5在这道题中是很特许的,再使用01背包之前,我们首先要在现在所拥有的余额中保留5元,用这五元去购买最贵的物品,而剩下的钱就是背包的总容量,可以随意使用,因此可得代码.

#include<bits/stdc++.h>
using namespace std;
int dp[51010],n,m,a[1010];
int main()
{
    while(cin>>n&&n)
    {
        memset(dp,0,sizeof(dp));
        memset(a,0,sizeof(a));
        for(int i=0;i<n;i++) cin>>a[i];
        cin>>m;
        if(m<5){
            cout<<m<<endl;
            continue;
        }
        m-=5;
        sort(a,a+n);
        for(int i=0;i<n-1;i++)
            for(int j=m;j>=a[i];j--)
           dp[j]=max(dp[j],dp[j-a[i]]+a[i]);

        cout<<m-a[n-1]-dp[m]+5<<endl;
    }
}

3.HDU1171:Big Event in HDU
http://acm.hdu.edu.cn/showproblem.php?pid=1171
题意:01背包的变形,给你物品和种类,分成平均两部分,两部分分之差最小。先以sum/2去选择最多质量的物品,求得结果小于等于另一半。

#include<cstring>
#include<climits>
#include<cstdio>
#include<cstdlib>
#include<map>
#include<stack>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
int dp[255010],val[5010];
int main()
{
    int n;
    while(cin>>n){
        memset(dp,0,sizeof(dp));
     if(n<=0) break;
     int sum=0,t=0;
     for(int i=0;i<n;i++){
        int a,b; cin>>a>>b;
        sum+=a*b;
        for(int j=0;j<b;j++)
            val[t++]=a;
     }
     for(int i=0;i<t;i++)
        for(int j=sum/2;j>=val[i];j--)
        dp[j]=max(dp[j],dp[j-val[i]]+val[i]);
     cout<<max(sum-dp[sum/2],dp[sum/2])<<" "<<min(sum-dp[sum/2],dp[sum/2])<<endl;
    }
}

4.HDU2639:Bone Collector II(01背包第k优解)
http://acm.hdu.edu.cn/showproblem.php?pid=2639
题意:这题不是01背包最优解,而是第k优解
思路:这里加一维 ,每个状态下第k解,dp[n][k],打个比方,一个年级有几个班,你要知道年级第一,你就需要知道每个班第一,你要知道年级前k名,你就要知道每个班前k名,所以这里,求出每个决策k个解,在排序后转移。

#include<bits/stdc++.h>
using namespace std;
int dp[1010][50],value[110],cost[110],A[50],B[50];
int main()
{
    int T;cin>>T;
    while(T--){
        memset(dp,0,sizeof(dp));
        int n,v,k; cin>>n>>v>>k;
        for(int i=0;i<n;i++) cin>>value[i];
        for(int i=0;i<n;i++) cin>>cost[i];
        for(int i=0;i<n;i++)
        {
            for(int j=v;j>=cost[i];j--)
            {
                for(int kk=0;kk<k;kk++)
                {
                    A[kk]=dp[j-cost[i]][kk]+value[i];
                    B[kk]=dp[j][kk];
                }
                A[k]=-1,B[k]=-1;
                int a=0,b=0,c=0;
                while(c<k&&(a<k||b<k))
                {
                    if(A[a]>B[b]) dp[j][c]=A[a],a++;
                    else dp[j][c]=B[b],b++;
                    if(dp[j][c]!=dp[j][c-1]) c++;
                }
            }
        }
        cout<<dp[v][k-1]<<endl;
    }
}

5.HDU2955:Robberies
http://acm.hdu.edu.cn/showproblem.php?pid=2955
题意:这题带了点概率的知识,实质还是01背包选择问题,要求不被抓的概率小于一个范围,多个独立事件的概率等于他们的积,注意题目输入是被抓的总概率,和每个银行被抓的概率,注意取反。
定义dp[sum]抢到金额为sum不被抓的概率.
找到概率大于等于题目要求最大值。

#include<bits/stdc++.h>
using namespace std;
int v[110];
double dp[10010],p[110];
int main()
{
    int T;cin>>T;
    while(T--)
    {
        int sum=0;
        double P,N;cin>>P>>N;
        P=1-P;
        for(int i=0;i<N;i++)
        {
            cin>>v[i]>>p[i];
            sum+=v[i];
            p[i]=1-p[i];
        }
        memset(dp,0,sizeof(dp));
        dp[0]=1.0;
        for(int i=0;i<N;i++)
            for(int j=sum;j>=v[i];j--)
              dp[j]=max(dp[j],dp[j-v[i]]*p[i]);
        for(int i=sum;i>=0;i--)
        {
            if(dp[i]-P>0.00000001){
                cout<<i<<endl;
                break;
            }
        }
    }
}

6.HDU3466:Proud Merchants
http://acm.hdu.edu.cn/showproblem.php?pid=3466
题意:买东西,每个东西有三个特征值,p代表价格,q代表你手中钱必须不低于q才能买这个物品,v代表得到的价值。

mark:又是变种01背包,每做一个变种的,就是一种提高。。

   这题因为涉及到q,所以不能直接就01背包了。因为如果一个物品是5 9,一个物品是5 6,对第一个进行背包的时候只有dp[9],dp[10],…,dp[m],再对第二个进行背包的时候,如果是普通的,应该会借用前面的dp[8],dp[7]之类的,但是现在这些值都是0,所以会导致结果出错。
于是要想到只有后面要用的值前面都可以得到,那么才不会出错。设A:p1,q1 B:p2,q2,如果先A后B,则至少需要p1+q2的容量,如果先B后A,至少需要p2+q1的容量,那么就是p1+q2 > p2+q1,变形之后就是q1-p1 < q2-p2。

#include<bits/stdc++.h>
using namespace std;
struct node
{
    int p,q,v;
}a[555];
int cmp(node a,node b)
{
    return a.q-a.p<b.q-b.p;
}
int dp[5555];
int main()
{
    #ifdef yxj
    freopen("F:\\in.txt","r",stdin);
    #endif // yxj
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=0;i<n;i++) scanf("%d %d %d",&a[i].p,&a[i].q,&a[i].v);
        sort(a,a+n,cmp);
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;i++)
            for(int j=m;j>=a[i].q;j--)
            dp[j]=max(dp[j],dp[j-a[i].p]+a[i].v);

        printf("%d\n",dp[m]);
    }
}

7.HDU1864:最大报销额
http://acm.hdu.edu.cn/showproblem.php?pid=1864
题目中药注意的有几样,首先每张发票中单件物品价格不能超过600,其次发票总额不能超过1000,而且发票上的物品必须是ABC三类,将满足以上条件的发票存入数组之中,就是裸01背包

#include <fstream>
#include <iostream>
#include <string>
#include <complex>
#include <math.h>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <stdio.h>
#include <stack>
#include <algorithm>
#include <list>
#include <ctime>
#include <memory.h>
#include <ctime>
#include <assert.h>

#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define eps 1e-8
#define M_PI 3.141592653589793

typedef long long ll;
const ll mod=1000000007;
ll powmod(ll a,ll b) {ll res=1;a%=mod;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}

using namespace std;
const int N=3000010;
int dp[N];
int p[50];

int main()
{
    #ifdef yxj
    freopen("F:\\in.txt","r",stdin);
    #endif // yxj
    double q;
    int n;
    while(cin>>q>>n&&n)
    {
        int cnt=0,Q=(int)(q*100);
        for(int i=0;i<n;i++)
        {
             int t,a=0,b=0,c=0,flag=0;
             scanf("%d",&t);
             for(int j=0;j<t;j++)
             {
                 char C; double num;
                 scanf(" %c:%lf",&C,&num);
              //   cout<<c<<" "<<num<<endl;
                 if(C=='A') a+=num*100;
                 else if(C=='B') b+=num*100;
                 else if(C=='C') c+=num*100;
                 else flag=1;
                 if(a+b+c>100000||a>60000||b>60000||c>60000) flag=1;

             }
            // cout<<a+b+c<<endl;
            //cout<<"flag="<<flag<<endl;
            if(flag) continue;
             else p[cnt++]=a+b+c;
        }
       // cout<<cnt<<endl;
        memset(dp,0,sizeof(dp));
        //rep(i,0,cnt) cout<<p[i]<<endl;
        for(int i=0;i<cnt;i++)
        {
            for(int j=Q;j>=p[i];j--)
                dp[j]=max(dp[j],dp[j-p[i]]+p[i]);
        }
        printf("%.2f\n",dp[Q]/100.0);
    }
}

8.世界。hrbust 2252
http://acm.hrbust.edu.cn/index.php?m=ProblemSet&a=showProblem&problem_id=2252
题意:完全背包,每个勇气(背包)有花费,和勇气值(价值),每个勇气数量无限,给你一个目标勇气值,问最小的花费。就是类似告诉你 总价值,求背包最小容量。

正常求前i个背包里,花费多少得到勇气最大值。再从小往上找第一个勇气值大于等于目标值的花费,并输出,按照这个dp这里花费上界得确定找一个最大的作为上届。
还可以 定义,前i个到达勇气值j的最小值。

#include <fstream>
#include <iostream>
#include <string>
#include <complex>
#include <math.h>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <stdio.h>
#include <stack>
#include <algorithm>
#include <list>
#include <ctime>
#include <memory.h>
#include <ctime>
#include <assert.h>

#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define eps 1e-8
#define M_PI 3.141592653589793

typedef long long ll;
const ll mod=1000000007;
const int inf=99999999;
ll powmod(ll a,ll b) {ll res=1;a%=mod;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}

using namespace std;
int cost[110],w[110];
/*
int main()
{
    int T;cin>>T;
    while(T--){
        int n,m,sum=0;
        cin>>n>>m;
        for(int i=0;i<n;i++){
          cin>>cost[i]>>w[i];
          sum=max(sum,((m/w[i])+1)*cost[i]);
        }
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;i++)
          for(int j=cost[i];j<=sum;j++)
            dp[j]=max(dp[j],dp[j-cost[i]]+w[i]);
        for(int i=1;i<=sum;i++)
            if(dp[i]>=m)
        {
            cout<<i<<endl;
            break;
        }
    }
}
*/
int dp[2100];//勇气值为m的最小花费.
int main()
{
    int T;cin>>T;
    while(T--){
        int n,m,maxx=0;cin>>n>>m;
        for(int i=0;i<n;i++)
            cin>>cost[i]>>w[i],maxx=max(maxx,w[i]);
        for(int i=0;i<=m+maxx;i++) dp[i]=inf;
        dp[0]=0;
        for(int i=0;i<n;i++)
          for(int j=w[i];j<=m+maxx;j++)
            dp[j]=min(dp[j],dp[j-w[i]]+cost[i]);
         int res=inf;
        for(int i=m+maxx;i>=m;i--)
        {
            //cout<<dp[i]<<endl;
            res=min(res,dp[i]);
        }
        cout<<res<<endl;
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值