首先01背包题目的雏形是

N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。

从这个题目中可以看出,01背包的特点就是:每种物品仅有一件,可以选择放或不放。

其状态转移方程是:

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

对于这方方程其实并不难理解,方程之中,现在需要放置的是第i件物品,这件物品的体积是c[i],价值是w[i],因此f[i-1][v]代表的就是不将这件物品放入背包,而f[i-1][v-c[i]]+w[i]则是代表将第i件放入背包之后的总价值,比较两者的价值,得出最大的价值存入现在的背包之中。

理解了这个方程后,将方程代入实际题目的应用之中,可得

for(i = 1; i<=n; i++)  
{  
    for(j = v; j>=c[i]; j--)//在这里,背包放入物品后,容量不断的减少,直到再也放不进了  
    {  
        f[i][v]=max(f[i-1][v],f[i-1][j-c[i]]+w[i]);  
    }  
}  

hdu2546

01背包,先对菜进行从小到大排序。

用m-5对前n-1个菜进行01背包,最后减去最贵的菜即可

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 1010
int n,m;
int dp[maxn],v[maxn];
int main()
{
    while(~scanf("%d",&n))
    {
        if(n==0)break;
        for(int i=1;i<=n;i++)scanf("%d",&v[i]);
        sort(v+1,v+n+1);
        scanf("%d",&m);
        if(m<5)
        {
            printf("%d\n",m);
            continue;
        }
        memset(dp,0,sizeof(dp));
        m-=5;
        for(int i=1;i<n;i++)
        {
            for(int j=m;j>=v[i];j--)//01pack
            {
                dp[j]=max(dp[j],dp[j-v[i]]+v[i]);
            }
        }
        printf("%d\n",m+5-dp[m]-v[n]);
    }
    return 0;
}
View Code

hdu1171

要求将物品尽量按照价值等分成两份,先对物品求和sum

对sum/2进行01背包

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int v[5500];
int dp[250050];
int sum,cnt;
int main()
{
    int n;
    while(~scanf("%d",&n)&&n>0)
    {
        sum=0,cnt=0;
        for(int i=0;i<n;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            while(b--)
            {
                v[cnt++]=a;
                sum+=a;
            }
        }
        memset(dp,0,sizeof(dp));
        for(int i=0;i<cnt;i++)
        {
            for(int j=sum/2;j>=v[i];j--)
            {
                dp[j]=max(dp[j],dp[j-v[i]]+v[i]);
            }
        }
        printf("%d %d\n",sum-dp[sum/2],dp[sum/2]);

    }
    return 0;
}
View Code

hdu2602

给你n个物体的价值和体积,背包的体积,求最大价值

裸01背包

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int v[1100],c[1100];
int dp[1100];
int main()
{
    int T;
    scanf("%d",&T);
    int n,m;
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)scanf("%d",&v[i]);
        for(int i=0;i<n;i++)scanf("%d",&c[i]);
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;i++)
        {
            for(int j=m;j>=c[i];j--)
            {
                dp[j]=max(dp[j],dp[j-c[i]]+v[i]);
            }
        }
        printf("%d\n",dp[m]);
    }
}
View Code

hdu2639

01背包第k优解

首先dp[i][j]代表的是在体积为i的时候第j优解为dp[i][j]......那么,我们就可以这样思考,i对应体积,那么如果只是一维的dp[i],代表的应该是体积为i时的最大值,那么同理,dp[i][1]代表的是体积为i时的最大值,那么我们就可以推出两种状态,dp[i][d],dp[i-c[i]][d]+v[i].....然后把这两种状态开个两个数组分别保存起来,再合并出体积为i时的前k优解......依次后推,直到dp[m][k].......

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
int v[1100],c[1100];
int dp[1100][50];
int a[50],b[50];
bool cmp(int a,int b)
{
    return a>b;
}
int main()
{
    int T;
    scanf("%d",&T);
    int n,m,k;
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&k);
        for(int i=0;i<n;i++)scanf("%d",&v[i]);
        for(int i=0;i<n;i++)scanf("%d",&c[i]);
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;i++)
        {
            for(int j=m;j>=c[i];j--)
            {
                for(int d=1;d<=k;d++)
                {
                    a[d]=dp[j-c[i]][d]+v[i];
                    b[d]=dp[j][d];
                }
                int x=1,y=1,z=1;
                a[k+1]=b[k+1]=-1;//一旦x或者y超过最k后要给x,y赋值最小值,以免误判
                while(z<=k&&(x<=k||y<=k))
                {
                    if(a[x]>b[y])
                        dp[j][z]=a[x++];
                    else
                        dp[j][z]=b[y++];
                    if(dp[j][z]!=dp[j][z-1]){
                        z++;
                    }
                }
            }
        }
        printf("%d\n",dp[m][k]);
    }
}
View Code

hdu2955

01背包,不被逮捕的概率作为物品的价值,dp[i]表示得到i元物品时不被逮捕的概率的最大值,最后从sum往前遍历,找到的第一个即是答案

注意初始化,01背包乘法时dp[0]=1.0 ,dp[1-sum]=0

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
int c[110];
double dp[10010],v[110];
int main()
{
    int T;
    scanf("%d",&T);
    int n;
    double p;
    while(T--)
    {
        dp[0]=1.0;
        for(int i=1;i<10010;i++)dp[i]=0;
        int sum=0;
        scanf("%lf%d",&p,&n);
        p=1-p;
        for(int i=0;i<n;i++)
        {
            scanf("%d%lf",&c[i],&v[i]);
            v[i]=1-v[i];
            sum+=c[i];
        }
        for(int i=0;i<n;i++)
        {
            for(int j=sum;j>=c[i];j--)
            {
                dp[j]=max(dp[j],dp[j-c[i]]*v[i]);
            }
        }
        for(int i=sum;i>=0;i--)
        {
            if(dp[i]>p)
            {
                printf("%d\n",i);
                break;
            }
        }
    }
}
View Code

hdu3466

比如对 3 5 6,5 10 5这两个物品,如果我们的决策是两个都不选或者是只选其中一个,显然没什么问题,但如果我们要是两个都选的话,按照之前这个顺序有m>=13,但如果把两个的顺序交换一下则m>=10即可,从这里就可以看出问题的所在了。于是,对任意两个物品i,j,为了避免上面存在的那种问题,我们可以算出两种顺序所需要的最少金额,若i->j,则至少需要Pi+Qj,若是j->i则至少需要Pj+Qi。如果已知结果是i->j较优的话,则有Pi+Qj<Pj+Qi,即Qi-Pi>Qj-Pj。所以若对之前的物品先按照Q-P由大到小排好序后

然后就是普通的01背包

写的时候是q-p从小到大排,讲不清原因..留坑

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct ss
{
    int p,q,v;
}a[550];
int dp[500050];
bool cmp(ss a,ss b)
{
    return a.p+b.q>a.q+b.p;
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        memset(dp,0,sizeof(dp));
        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);
        for(int i=0;i<n;i++)
        {
            for(int j=m;j>=a[i].q;j--)//a[i].q>=a[i].p
            {
                dp[j]=max(dp[j],dp[j-a[i].p]+a[i].v);
            }
        }
        printf("%d\n",dp[m]);
    }
    return 0;
}
View Code

 hdu1864

选出可以报销的发票进行01背包即可

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define maxn 3000030
int n,m;
int v[50];
int dp[maxn];
int main()
{
    double tmp;
    while(~scanf("%lf%d",&tmp,&n)&&n)
    {
        int cnt=0;
        m=tmp*100;
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;i++){
            int k;
            int a[3]={0};
            scanf("%d",&k);
            int flag=1;
            while(k--)
            {
                char ch[20];
                scanf("%s",ch);
                sscanf(ch+2,"%lf",&tmp);
                int t=tmp*100;
                if(ch[0]=='A'||ch[0]=='B'||ch[0]=='C'){
                        a[ch[0]-'A']+=t;
                        if(a[ch[0]-'A']>60000)
                            flag=0;
                }
                else flag=0;
            }
            if(a[0]+a[1]+a[2]<=100000&&flag)
            {
                v[cnt++]=a[0]+a[1]+a[2];
            }
        }

        for(int i=0;i<cnt;i++)
        {
            for(int j=m;j>=v[i];j--)
            {
                dp[j]=max(dp[j],dp[j-v[i]]+v[i]);
            }
        }
        printf("%.2lf\n",dp[m]*1.0/100);
    }
    return 0;
}
View Code

 

posted on 2015-04-19 19:27  kylehz  阅读(172)  评论(0编辑  收藏  举报