dp之背包总结篇

前言:背包问题在dp中可以说是经典,作为一个acmer,到现在才正式学习dp,可以说是比较失败的。我个人比较认同一点,想要做一个比较成功的acmer,dp、搜索、数学必须精练,比较遗憾的是,对我我自身而言,并没有早早的认识到这个问题,不过现在知道了,还有一年,也不算晚。还有,我建议学背包的童鞋,都看背包九讲…… dp之01背包 01背包,做为背包中最基础的一类背包,必须要掌握好,当然我这里说的掌
摘要由CSDN通过智能技术生成

前言:背包问题在dp中可以说是经典,作为一个acmer,到现在才正式学习dp,可以说是比较失败的。我个人比较认同一点,想要做一个比较成功的acmer,dp、搜索、数学必须精练,比较遗憾的是,对我我自身而言,并没有早早的认识到这个问题,不过现在知道了,还有一年,也不算晚。还有,我建议学背包的童鞋,都看背包九讲……
dp之01背包
01背包,做为背包中最基础的一类背包,必须要掌握好,当然我这里说的掌握好,并不是说,你横扫hdu或者poj等oj上01背包模板题就可以的,记得很久以前,刚开始做背包问题,一天在hdu水了七八道,就自以为背包就是个模板,唉,真心不知道当时的自己是有多肤浅……….
01背包的动态转移方程,背包九讲上有详细的讲解,这里不再复述,就想说下,一维的和二维的转移方程都需要深刻理解,不要觉得任何时候01背包都可以转化为1维的数组
dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+val[i]);
dp[j]=max(dp[j],dp[j-v[i]]+val[i]);
此外,还想说下,01背包为什么体积要从v—0?这个问题,背包九讲上是说可以每件物品只拿一次,恩,我觉得可以自己推推,这样理解会更加深刻…….
1、UVA624(记录路径问题)
总得来说,不管是01背包还是完全背包,其动态转移每次只有两种状态在转移,就说这道题目,
dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+val[i]) 对于dp[i][j]来说,它只能使由两个状态中的一个转移过来的,要么取一件,要么不取,那么我们再开一个二维数组s[i][j],0表示不取,1表示取,那么不取是dp[i-1][j],取是dp[i-1][j-v[i]]+val[i];这样,数组s[i][j]就记录下了每次动态转移的方向,在递归调用,打印路径,结果就出来了,具体可以看http://www.cnblogs.com/ziyi–caolu/archive/2013/04/10/3012578.html
2、UVA562(平分钱币问题)
题目大意:给定n个硬币,要求将这些硬币平分以使两个人获得的钱尽量多,求两个人分到的钱最小差值。
这类问题的关键就在平衡,而就这到题目而言,01背包本身求的就是在体积小于等于sum的情况下的最大值,那么这些硬币本身既当体积又当价值,把总价值求出来/2,再套用模板……可以说比较水的平衡问题,具体看:http://www.cnblogs.com/ziyi–caolu/archive/2013/04/10/3012755.html
3、组成n块钱,有多少种的问题
这类题目,一般是这样描述的:给你n块钱,有m种钱币,每种钱币只能用一次,问组成n块钱有多少种方法……..
对于这类题目,我更多的感受是觉得是到递推题,其实,有好多的dp都是一个递推,然后记录更新状态的过程,要组成dp[j],那么假设有一个5快的钱币,那么首先要判断dp[j-5]有没有组成,这里我比较喜欢用0表示没有这个状态,>0表示这个状态存在,那么在初始化dp数组的时候,dp[0]=1;然后套用背包模板,其动态转移方程(倒不如说递推式)为dp[j]+=dp[j-5];
上面是常规题目,下面为01背包的变种题:
4、poj2184(两个属性最大和,平移问题)
题意:给定两个属性,求这两个属性的和的最大值………
思路:对于这种题目,我们可以尝试将一个属性当作容量,另一个属性当作价值,然后考虑dp…….这个题目属性还可以是负数,那么可以采取平移1000个单位,使其不存在负数问题。将第一个属性往后平移1000个单位,然后推导其动态转移方程,若是dp[i],代表当加入第一个属性加到i时,符合题意的第二个属性的最大值……题意是两个属性的和的最大值,那么动态转移方程必然不是dp[j]=max(dp[j],dp[j-s[i][0]]+s[i][1]),因为这个动态转移方程固然可以求出第二个属性的最大值,但别忘了题意要求第一个属性与第二个属性的和的最大值,那么,第一个属性平移了1000个单位,在考虑动态转移时,是必须要将这个考虑进去的。可以开一个a数组记录路径,然后根据题意,
动态转移方程应该为dp[j]=max(dp[j]-a[j]*1000,dp[j-s[i][0]]+s[i][1]-(a[j-s[i][0]]+1)*1000),一开始a数组全部赋值为0,所以需要a[j-s[i][0]]+1…..因为它新加入了一个值。考虑这个方程,dp数组的初始全部赋值为负无穷大,dp[0]=0。
注意一点,在历遍查找最大值的时候,dp[i]>0,i-a[i]*1000>0

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std; 
#define maxx 1000005
int s[maxx][2],dp[maxx],a[maxx];
int main()
{
    int n;
    while(scanf("%d",&n)>0)
    {
        int sum=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d %d",&s[i][0],&s[i][1]);
            if(s[i][0]<0&&s[i][1]<0)
            {
                i--;
                n--;
                continue;
            }
            s[i][0]+=1000;
            sum+=s[i][0];
        }
        memset(a,0,sizeof(a));
        for(int i=0;i<=sum;i++)
        dp[i]=-maxx;
        dp[0]=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=sum;j>=s[i][0];j--)
            if(dp[j]-a[j]*1000<dp[j-s[i][0]]+s[i][1]-(a[j-s[i][0]]+1)*1000)
            {
                dp[j]=dp[j-s[i][0]]+s[i][1];
                a[j]=a[j-s[i][0]]+1;
            }
        }
        int maxn=0;
        for(int i=1;i<=sum;i++)
        if(maxn<dp[i]-a[i]*1000+i&&dp[i]>=0&&i-a[i]*1000>=0)
        maxn=dp[i]-a[i]*1000+i;
        printf("%d\n",maxn);
    }
    return 0;
}

5、hdu2639(第k优解问题)
题意:给出一行价值,一行体积,让你在v体积的范围内找出第k大的值…….(注意,不要 和它的第一题混起来,它第一行是价值,再是体积)
思路:首先dp[i][j]代表的是在体积为i的时候第j优解为dp[i][j]……那么,我们就可以这样思考,i对应体积,那么如果只是一维的dp[i],代表的应该是体积为i时的最大值,那么同理,dp[i][1]代表的是体积为i时的最大值,那么我们就可以退出两种动态,dp[i][m],dp[i-s[i][0]][m]+s[i][1]…..然后把这两种状态开个两个数组分别保存起来,再合并出体积为i时的前k优解……依次后推,直到dp[v][k]…….


#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std;
#define max(x,y) x>y? x:y
typedef int ss;
ss dp[1005][100],s[1005][2];
ss a[50],b[50];

int main()
{
    int text;
    scanf("%d",&text);
    while(text--)
    {
        ss n,v,k;
        scanf("%d%d%d",&n,&v,&k);
        for(ss i=1;i<=n;i++)
        scanf("%d",&s[i][1]);
        for(ss i=1;i<=n;i++)
        scanf("%d",&s[i][0]);
        memset(dp,0,sizeof(dp));
        if(k==0)
        {
            printf("0\n");
            continue;
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=v;j>=s[i][0];j--)
            {
                for(int m=1;m<=k;m++)
                {
                    a[m]=dp[j][m];
                    b[m]=dp[j-s[i][0]][m]+s[i][1];
                }
                int x=1,y=1,w=1;
                a[k+1]=b[k+1]=-1;  //这个地方要注意,因为有可能a或者b数组先比较完 

                while(w<=k&&(x<=k||y<=k))
                {
                    if(a[x]>b[y])
                    {
                        dp[j][w]=a[x++];
                    }
                    else
                    {
                        dp[j][w]=b[y++];
                    }
                    if(w==1||dp[j][w]!=dp[j][w-1])       //这是去重..... 
                    w++;
                    //printf("111\n");
                }
            }

        }
        //for(int i=1;i<=k;i++)
    printf("%d\n",dp[v][k]);
    }
    return 0;
} 

6、hdu3644(带限制的01背包)
题意:买东西,每个东西有三个特征值,p代表价格,q代表你手中钱必须不低于q才能买这个物品,v代表得到的价值。
mark:又是变种01背包,每做一个变种的,就是一种提高。。
按照q - p以由大到小的顺序排序,然后进行01背包的DP即可。

#include<stdio.h>
#include<algorithm>
#include<iostream>
using namespace std;
const int MAXN=5005;
int dp[MAXN];
struct Node
{
    int p,q,v;
}node[505];
bool cmp(Node a,Node b)
{
    return  (a.q-a.p)<(b.q-b.p);
}    
int main()
{
    int n,m;
    int i,j;
    int p,q,v;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(i=0;i<=m;i++)
          dp[i]=0;
        
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值