Lintcode背包问题一窝端

今天来对Lintcode上面的背包专题来个总结,除了付费的题全都做完了,就来回顾一下;
不得不说,做题自己悟永远是王道,北大版的《背包九讲》我是硬生生没有看下去,衰······
言归正传,让我们开始正文,这篇文章会贼长的我感觉······

一.不可分割不可重复使用的背包问题

  1. 背包问题
    在n个物品中挑选若干物品装入背包,最多能装多满?假设背包的大小为m,每个物品的大小为A[i]
int backPack(int m, vector<int> &A) {
        // write your code here
        vector<int>dp(m+1,0);
        for(int i=0;i<A.size();i++)
        {
            for(int j=m;j>=A[i];j--)
            {
                dp[j]=max(dp[j],dp[j-A[i]]+A[i]);
            }
        }
        return dp.back();
    }
  1. 背包问题 II
    中文
    English
    有 n 个物品和一个大小为 m 的背包. 给定数组 A 表示每个物品的大小和数组 V 表示每个物品的价值.
    问最多能装入背包的总价值是多大?
    int backPackII(int m, vector<int> &A, vector<int> &V) {
        // write your code here
        vector<int>dp(m+1,0);
        for(int i=0;i<A.size();i++)
        {
            for(int j=m;j>=A[i];j--)
            {
                dp[j]=max1(dp[j],dp[j-A[i]]+V[i]);
            }
        }
        return dp[m];
  
    }
  1. 卡牌游戏 II
    中文
    English
    你跟你的朋友在玩一个卡牌游戏,总共有 n 张牌。每张牌的成本为 cost[i] 并且可以对对手造成 damage[i] 的伤害。你总共有 totalMoney 元并且需要造成至少 totalDamage 的伤害才能获胜。每张牌只能使用一次,判断你是否可以取得胜利。
 bool cardGame(vector<int> &cost, vector<int> &damage, int totalMoney, int totalDamage) {
        // Write your code here
        vector<int>dp(totalMoney+1,0);
        dp[0]=0;
        for(int i=0;i<cost.size();i++)
        {
            for(int j=totalMoney;j>=cost[i];j--)
            {
                dp[j]=max(dp[j],dp[j-cost[i]]+damage[i]);
            }
        }
        
        if(dp[totalMoney]>=totalDamage)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
  1. 背包问题 V
    中文
    English
    给出 n 个物品, 以及一个数组, nums[i] 代表第i个物品的大小, 保证大小均为正数, 正整数 target 表示背包的大小, 找到能填满背包的方案数。
    每一个物品只能使用一次
int backPackV(vector<int> &nums, int target) {
        // write your code here
        vector<int>cp(target+1,0);
        cp[0]=1;
        for(int i=0;i<nums.size();i++)
        {
            for(int j=target;j>=nums[i];j--)
            {
                cp[j]+=cp[j-nums[i]];
            }
        }
        return cp[target];
    }

先来这四道题,其实这四道题是一个类型的题目,特点: 物品不能重复使用;物品不得分割。
其中,物体不能分割是次要的,而程序主要需要应付是“不能重复使用!”,因此,才会使用形如

for(nums[i],i++)
{
	for(target,target--)
}

的循环,内嵌循环之所以需要由大到小的原因很多博客都讲过了,我还是想赘述一遍。。。
如果从小到大,同一物体就有可能出现在某一较大容器中两次(只可意会啊果然)。
这还是我在夜游成府路的时候想出来的。。。

二.不可分割可重复的背包问题
当物品可以重复使用时,就可以放飞自我了!
内嵌循环由小到大,并且可以改变内外循环的顺序!
举例:
562. 背包问题 IV
给出 n 个物品, 以及一个数组, nums[i]代表第i个物品的大小, 保证大小均为正数并且没有重复, 正整数 target 表示背包的大小, 找到能填满背包的方案数。
每一个物品可以使用无数次

int backPackIV(vector<int> &nums, int target) {
        // write your code here
        vector<int>dp(target+1,0);
        dp[0]=1;
        for(int i=0;i<nums.size();i++)
        {
            for(int j=0;j<=target;j++)
            {
                if(j>=nums[i])
                {
                dp[j]+=dp[j-nums[i]];
                }
            }
        }
        return dp[target];
    }
  1. 换硬币
    给出不同面额的硬币以及一个总金额. 写一个方法来计算给出的总金额可以换取的最少的硬币数量. 如果已有硬币的任意组合均无法与总金额面额相等, 那么返回 -1.
    int coinChange(vector<int> &coins, int amount) {
        if(amount==0)
        {
            return 0;
        }
        vector<int>dp(amount+1,INT_MAX);
        dp[0]=0;
            for(int j=0;j<coins.size();j++)
            {
                for(int i=1;i<=amount;i++)
            {
                if(i>=coins[j]&&dp[i-coins[j]]!=INT_MAX)
                {
                dp[i]=min(dp[i],dp[i-coins[j]]+1);
                }
            }
        }
        if(dp[amount]==INT_MAX)
        {
            return -1;
        }
        else
        {
            return dp[amount];
        }
    }
  1. 约翰的后花园
    约翰想在他家后面的空地上建一个后花园,现在有两种砖,一种3 dm的高度,7 dm的高度。约翰想围成x dm的墙。如果约翰能做到,输出YES,否则输出NO。
string isBuild(int x) {
        // write you code here
        if(x<3||(x>3&&x<6))
        {
            return "NO";
        }
        if(x==6||x==3||x==7)
        {
            return "YES";
        }
        vector<int>dp(x+1,0);
        vector<int>sub{3,7};
        dp[3]=3;
        dp[4]=3;
        dp[5]=3;
        dp[6]=6;
        dp[7]=7;
        for(int i=8;i<x+1;i++)
        {
            if(i%3==0||i%7==0)
            {
                dp[i]=i;
            }
            else
            {
                for(int j=0;j<sub.size();j++)
                {
                dp[i]=max(dp[i],dp[i-sub[j]]+sub[j]);
                }
            }
        }
        if(dp[x]==x)
        {
            return "YES";
        }
        else
        {
            return "NO";
        }
    }

后花园这个题有点意思;
这个题解是个玩笑。。。这里面其实有逻辑漏洞,但还是AC掉了。。。

for (int i = 0; i < 2; i++) { 
        for (int j = heights[i]; j <= x; j++) { 
            dp[j] = dp[j] || dp[j - heights[i]]; 
            // 本来就可以被围成 VS 之前可以被围成且加上当前这块砖可以被围成(扣去当前砖之前可以被围成)
        } 
    } 
    return dp[x] ? "YES" : "NO";

这个算是正解。

三.可分割可重复的背包问题

  1. 杆子分割
    给一个 n 英寸长的杆子和一个包含所有小于 n 的尺寸的价格. 确定通过切割杆并销售碎片可获得的最大值.
int cutting(vector<int> &prices, int n) {
        // Write your code here
        vector<int>dp(n+1,0);
            for(int j=0;j<prices.size();j++)
            {
                for(int i=1;i<n+1;i++)
                {   
                if(j+1<=i)
                {
                dp[i]=max(dp[i],dp[i-j-1]+prices[j]);
                }
            }
        }
        return dp[n];
    }

咳咳咳,可分割可重复和不可分割可重复其实解法一样啦。

四.乱入的动态规划
91. 最小调整代价
给一个整数数组,调整每个数的大小,使得相邻的两个数的差不大于一个给定的整数target,调整每个数的代价为调整前后的差的绝对值,求调整代价之和最小是多少。

我感觉这道题是乱入背包问题的动态规划题,当然啦,不得不承认是有关联性的,那就一起整了吧!

    int MinAdjustmentCost(vector<int> &A, int target) {
        // write your code here
        vector<vector<int>>dp(A.size(),vector<int>(101,INT_MAX));
        for(int i=1;i<101;i++)
        {
            dp[0][i]=abs(i-A[0]);
        }
        for(int i=1;i<A.size();i++)
        {
            for(int j=1;j<101;j++)
            {
                for(int k=1;k<=100;k++)
                {
                    if(target>=abs(j-k))
                    {
                    dp[i][j]=min(dp[i][j],dp[i-1][k]+abs(j-A[i]));
                    }
                }
            }
        }
        int res=INT_MAX;
        for(int i=1;i<101;i++)
        {
            res=min(res,dp[A.size()-1][i]);
        }
        return res;
    }

dp[i][j]的意义是,数组中前i个数字进行调整的最小代价,j=nums[i-1]。

结束!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值