01背包问题总结

来源:互联网 发布:ubuntu搭建samba 编辑:程序博客网 时间:2024/04/27 21:58

总结一下 按照: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.141592653589793typedef 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.141592653589793typedef 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;    }}
0 0
原创粉丝点击