码迷,mamicode.com
首页 > 其他好文 > 详细

01背包

时间:2016-07-19 10:22:43      阅读:150      评论:0      收藏:0      [点我收藏+]

标签:

状态转移方程:dp[i][j] = max(dp[i-1][j], dp[i-1][j-c[i]] + w[i]) (正背)
可简化为一维方程:dp[i] = max(dp[i], dp[i-c[i]] + w[i]) (倒背)
专题链接:01背包
密码:acmaker

A - Bone Collector

HDU 2602
很裸的背包,大意是把骨头放入包中,给出包的大小、骨头数量、没块骨头体积及价值,求能装下的骨头的最大价值。给出2种解法,也是01背包基本模板

//二维解法
#include"cstdio"
#include"iostream"
#include"algorithm"
#include"cstring"
#include"string" 
#include"cmath"
int c[1005],w[1005];        //c为每件物品代价,w为每件物品价值 
int dp[1005][1005]; 
int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        int N,V;
        scanf("%d%d",&N,&V);
        memset(c,0,sizeof c);
        memset(w,0,sizeof w);
        memset(dp,0,sizeof dp);
        int i;
        for(i=1;i<=N;i++)
            scanf("%d",&w[i]);
        for(i=1;i<=N;i++)
            scanf("%d",&c[i]);

        for(i=1;i<=N;i++)
            for(int j=0;j<=V;j++)       //正背包
                if(c[i]<=j)     //判断当前物品是否可以入包 
                    dp[i][j]=max(dp[i-1][j],dp[i-1][j-c[i]]+w[i]);      //真·状态转移方程 
                else
                    dp[i][j]=dp[i-1][j];

        int ans=0;
        for(i=0;i<=V;i++)       //检索最大值 
        {
            if(dp[N][i]>ans)ans=dp[N][i];
        }
        printf("%d\n",ans);
    }
    return 0;
}
//一维解法 
#include"cstdio"
#include"iostream"
#include"algorithm"
#include"cstring"
#include"string" 
#include"cmath"
int c[1005],w[1005];        //c为每件物品代价,w为每件物品价值 
int dp[1005]; 
int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        int N,V;
        scanf("%d%d",&N,&V);
        memset(c,0,sizeof c);
        memset(w,0,sizeof w);
        memset(dp,0,sizeof dp);
        int i;
        for(i=1;i<=N;i++)
            scanf("%d",&w[i]);
        for(i=1;i<=N;i++)
            scanf("%d",&c[i]);

        for(i=1;i<=N;i++)
            for(int j=V;j>=c[i];j--)        //倒背包 
                dp[j]=max(dp[j],dp[j-c[i]]+w[i]);       //状态转移方程 

        printf("%d\n",dp[V]);
    }
    return 0;
}

B - Charm Bracelet

POJ 3624
这题是珠宝,给出珠宝数量,重量上限,每个珠宝的重量和价值,求不超过上限的情况下珠宝的最高价值,也是裸背包。

#include"cstdio"
#include"iostream"
#include"algorithm"
#include"cstring"
#include"string" 
#include"cmath"
int c[3405],w[3405];        //c为每件物品代价,w为每件物品价值 
int dp[12890]; 
int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    int N,V;
    scanf("%d%d",&N,&V);
    memset(c,0,sizeof c);
    memset(w,0,sizeof w);
    memset(dp,0,sizeof dp);
    int i;
    for(i=1;i<=N;i++)
        scanf("%d%d",&c[i],&w[i]);

    for(i=1;i<=N;i++)
        for(int j=V;j>=c[i];j--)        //倒背包 
            dp[j]=max(dp[j],dp[j-c[i]]+w[i]);       //状态转移方程 

    printf("%d\n",dp[V]);
    return 0;
}

C - 饭卡

HDU 2546
饭卡余额大于等于5元可以任意购买(即使结账后余额为负),小于5元时不能购买(即使余额足够)。
给出的只有菜品的种类和单价,以及卡的余额。每种菜只能买一次。
这里菜的单价既是代价c,也是价值w卡的余额有5元的限制可以先将背包容量减5将菜品按价格排序,最贵的菜先不计算,最后在余额最少的情况下再减去最贵的菜即可。当然要保证此时的余额大于等于5元。

#include"cstdio"
#include"iostream"
#include"algorithm"
#include"cstring"
#include"string" 
#include"cmath"
using namespace std;
int c[1005];        //c为每件物品代价,同时为物品价值 
int dp[1005]; 
int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    int N;
    while(~scanf("%d",&N),N)
    {
        memset(c,0,sizeof c);
        memset(dp,0,sizeof dp);
        int i;
        for(i=1;i<=N;i++)
            scanf("%d",&c[i]);
        int V;
        scanf("%d",&V);
        if(V<5)     //余额不足 
        {
            printf("%d\n",V);
            continue;
        }
        V=V-5;
        sort(c+1,c+N+1);

        for(i=1;i<=N-1;i++)
            for(int j=V;j>=c[i];j--)        //倒背包 
                dp[j]=max(dp[j],dp[j-c[i]]+c[i]);       //状态转移方程 

        int ans=V+5-dp[V]-c[N];

        printf("%d\n",ans);
    }
    return 0;
}

D - CD

UVA 624
给出磁道长度,歌曲数量及每首歌的长度,求最大歌曲播放时间并给出播放的歌曲列表。
这里把歌曲时间长度作为代价,也作为价值,难点在于歌曲列表的记录。用vis数组来记录歌曲是否被添加,一旦dp[j]<=dp[j-c[i]]+c[i],就说明dp[i]的值要被改变,此时将vis[i][j]标记为1,在输出时对vis进行遍历即可。

#include"cstdio"
#include"iostream"
#include"algorithm"
#include"cstring"
#include"string" 
#include"cmath"
using namespace std;
int c[25];      //c为每件物品代价,w为每件物品价值 
int dp[10005];
int vis[25][10005];     //记录该歌曲是否被添加
int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    int N,V;
    while(~scanf("%d",&V))
    {
        memset(c,0,sizeof c);
        memset(dp,0,sizeof dp);
        memset(vis,0,sizeof vis);
        int i;
        scanf("%d",&N);
        for(i=1;i<=N;i++)
            scanf("%d",&c[i]);

        for(i=1;i<=N;i++)
            for(int j=V;j>=c[i];j--)        //倒背包 
            {
                if(dp[j]<=dp[j-c[i]]+c[i])
                {
                    dp[j]=dp[j-c[i]]+c[i];      //状态转移方程 
                    vis[i][j]=1;                //访问标记
                }
            }

        int j;
        for(j=V,i=N;i>0;i--)      //共N首歌曲
        {
            if(vis[i][j])
            {
                printf("%d ",c[i]);
                j-=c[i];        //寻找前一个歌曲
            }
        }

        printf("sum:%d\n",dp[V]);
    }
    return 0;
}

E - Dividing coins

UVA 562
给出硬币的数量和价值,2人分硬币,求2人所分价值的最小值。
将硬币的价值同时看做代价和价值背包容量设为硬币总价值的1/2,这样分出来的价值最大值就是最接近总价值1/2的小值,而另一人是大值,二人的价值差就等于总价值-背包最大值*2

#include"cstdio"
#include"iostream"
#include"algorithm"
#include"cstring"
#include"string" 
#include"cmath"
int c[10000];       //c为每件物品代价,w为每件物品价值 
int dp[50005]; 
int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        int N;
        scanf("%d",&N);
        int V=0;
        memset(c,0,sizeof c);
        memset(dp,0,sizeof dp);
        int i;
        for(i=1;i<=N;i++)
        {
            scanf("%d",&c[i]);
            V+=c[i];          //总价值
        }

        for(i=1;i<=N;i++)
            for(int j=V/2;j>=c[i];j--)      //倒背包 
                dp[j]=max(dp[j],dp[j-c[i]]+c[i]);       //状态转移方程 

        printf("%d\n",V-dp[V/2]*2);
    }
    return 0;
}

F - Robberies

HDU 2955
给出被捕的概率的安全上限,银行数,每家银行可获得的价值和被捕概率,求安全的情况下最多可以偷多少钱。
概率因为是小数,无法作为下标,所以用银行价值作为代价,概率作为价值,求在偷得某个价值的情况下的最大逃生率,再与安全逃生率比较,只要超过安全逃生线即可。
这里的概率要用乘法,因为逃生一次接一次,要在之前的逃生率上再重新计算逃生率。

#include"cstdio"
#include"iostream"
#include"algorithm"
#include"cstring"
#include"string" 
#include"cmath"
using namespace std;
int c[105];
double w[105];
double dp[10005];
double max(double a,double b)
{
    return a>b?a:b;
}
int main()
{
    int t;
    scanf("%d",&t);

    int N,V;
    double P;
    while(t--)
    {
        scanf("%lf %d",&P,&N);
        P=1-P;      //逃跑概率 
        memset(c,0,sizeof c);
        memset(w,0,sizeof w);
        memset(dp,0,sizeof dp);
        dp[0]=1;
        V=0;
        int i;
        for(i=1;i<=N;i++)
        {
            scanf("%d%lf",&c[i],&w[i]);
            V+=c[i];        //总金额
        }

        for(i=1;i<=N;i++)
            for(int j=V;j>=c[i];j--)        //倒背包 
            {
                dp[j]=max(dp[j],(1-w[i])*dp[j-c[i]]);       //状态转移方程,要注意这里逃跑概率要用乘法 
            }

        for(i=V;i>=0;i--)       //从钱多往钱少的方向遍历 
        {
            if(dp[i]>=P)    //逃跑概率足够大即可 
            {
                printf("%d\n",i);
                break;
            }
        }
    }
    return 0;
}

G - Cow Exhibition

POJ 2184
给出牛的数量和每头牛的智商及幽默感,选出若干头牛使智商总和及幽默感总和为正的情况下智商+幽默感总和的最大值。
难点在于如何保证智商和幽默感同时取到最大。奇淫奇巧:正背包+逆背包

#include"cstdio"
#include"iostream"
#include"algorithm"
#include"cstring"
#include"string" 
#include"cmath"
const int INF=-1<<31;
int c[1005],w[1005];        //c为智商,w为幽默感 
int dp[200005]; 
int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    int N;
    while(~scanf("%d",&N))
    {
        memset(c,0,sizeof c);
        memset(w,0,sizeof w);
        memset(dp,0,sizeof dp);
        int i;
        for(i=0;i<=200000;i++)  
            dp[i]=INF; 
        dp[100000]=0; 
        for(i=1;i<=N;i++)
            scanf("%d%d",&c[i],&w[i]);

        for(i=1;i<=N;i++)
        {
            if(c[i]<0 && w[i]<0)
                continue;
            else if(c[i]>0)
            {
                for(int j=200000;j>=c[i];j--)
                    if(dp[j-c[i]]>INF)
                        dp[j]=max(dp[j],dp[j-c[i]]+w[i]);
            }
            else
            {
                for(int j=c[i];j<=200000+c[i];j++)
                    if(dp[j-c[i]]>INF)
                        dp[j]=max(dp[j],dp[j-c[i]]+w[i]);
            }
        }
        int ans=INF;  
        for(i=100000;i<=200000;i++)
        //因为区间100000~200000才是表示的整数,那么此时的i就是之前背包中的s[i],如果此时dp[i]也就是f[i]大于等于0的话,我们再加上s[i](此时为i),然后减去作为界限的100000,就可以得到答案  
        {  
            if(dp[i]>=0)  
                ans=max(ans,dp[i]+i-100000);  
        }  
        printf("%d\n",ans);
    }
    return 0;
}

01背包

标签:

原文地址:http://blog.csdn.net/q390898664/article/details/51945434

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!