题目大意:输入n,代表有n种数,接下来n个数代表n种数,再接下来n个数代表每种数有多少个,在输入K,代表用这些数要加成的和

问你是否能加为K,能输出yes,不能输出no

这是一个典型的多重背包问题,可以用dp来求解,。但是如何定义递推关系会影响到最终的复杂度,首先我们先看一下如下定义:

dp[i+1][j];=用前i种数能否加成和为j

为了用前i种数加成j,也就需要能用前i-1种数字加成j,j-a[i],···,j-mi*a[i],中的某一种,由此我们可以定义如下递推关系

dp[i+1][j]=(0<=k<=mi,且k*a[i]<=j时,存在使dp[i][j-k*a[i]]为真的k;

看代码

  1. #include<iostream>
  2. #include<stdio.h>
  3. #include<string.h>
  4. #include<cmath>
  5. #include<math.h>
  6. #include<algorithm>
  7. #include<set>
  8. #include<queue>
  9. typedef long long ll;
  10. using namespace std;
  11. const ll mod=1e9+;
  12. #define INF 0x3f3f3f
  13. bool dp[][];
  14. int main()
  15. {
  16. memset(dp,false,sizeof(dp));
  17. dp[][]=true;//赋初值
  18. int n,s;
  19. int a[],b[];
  20. cin>>n;
  21. for(int i=;i<n;i++)
  22. {
  23. cin>>a[i];
  24. }
  25. for(int i=;i<n;i++)
  26. cin>>b[i];
  27. cin>>s;
  28. //for(int i=;i<n;i++)
  29. // cout<<a[i]<<' '<<b[i]<<endl;
  30. for(int i=;i<n;i++)
  31. {
  32. for(int j=;j<=s;j++)
  33. {
  34. for(int k=;k<=b[i]&&k*a[i]<=j;k++)
  35. {
  36. dp[i+][j]|=dp[i][j-k*a[i]];//注意这里的或运算,代表有一个为真则为真
  37. }
  38. }
  39. }
  40. //for(int i=;i<=s;i++)
  41. // cout<<dp[n][i]<<' ';
  42. if(dp[n][s])
  43. cout<<"yes"<<endl;
  44. else
  45. cout<<"no"<<endl;
  46. return ;
  47. }

上面这个算法的复杂度是比较大,并不够好。一般用dp求取bool 结果的话会有不少浪费,同样的复杂度通常能获得更多的信息

在这个问题中,我们不光求出能否得到目标的和数,同时把得到时a[i]这个数还剩多少个计算出来,这样就可以减少复杂度

dp[i+][j]:=用前i种数加和得到j时,第i种数还剩多少个(不能加的情况为-1)

按照如上所述的递推关系,这样如果前i-1个数加能得到j的话,第i个数就可以留下b[i]个了,此外,前i种数加和出j-a[i]时第i种数还剩k(k>0)个的话

,用这i种数加和为j时就能剩k-1个了,由此我们可以得出如下递推式:

dp[i+1][j]=b[i]     (dp[i][j]>=0)

dp[i+1][j]=-1       (j<a[i]||dp[i_1][j-a[i]]<=0)

dp[i+1][j]=dp[i+1][j-a[i]]-1     (其它)

看代码

  1. #include<iostream>
  2. #include<stdio.h>
  3. #include<string.h>
  4. #include<cmath>
  5. #include<math.h>
  6. #include<algorithm>
  7. #include<set>
  8. #include<queue>
  9. typedef long long ll;
  10. using namespace std;
  11. const ll mod=1e9+;
  12. #define INF 0x3f3f3f
  13. int dp[][];
  14. int main()
  15. {
  16. memset(dp,-,sizeof(dp));
  17. dp[][]=;//赋初值
  18. int n,s;
  19. int a[],b[];
  20. cin>>n;
  21. for(int i=;i<n;i++)
  22. {
  23. cin>>a[i];
  24. }
  25. for(int i=;i<n;i++)
  26. cin>>b[i];
  27. cin>>s;
  28. for(int i=;i<n;i++)
  29. {
  30. for(int j=;j<=s;j++)
  31. {
  32. if(dp[i][j]>=)
  33. dp[i+][j]=b[i];
  34. else if(j<a[i]||dp[i+][j-a[i]]<=)
  35. dp[i+][j]=-;
  36. else
  37. dp[i+][j]=dp[i+][j-a[i]]-;
  38.  
  39. }
  40. }
  41. if(dp[n][s]>=)
  42. cout<<"yes"<<endl;
  43. else
  44. cout<<"no"<<endl;
  45. return ;
  46. }

题目大意:有一个长度为n的序列,a[0],a[1]···a[n-1],请求出这个序列中最长的上升子序列的长度,上升子序列可以不连续,任意i<j,a[i]<a[j]

限制条件:1<=n<=1000   1<=a[i]<=1000000

首先我们建立递推关系

定义dp[i]=:以a[i]为末尾的最长上升子序列的长度

以a[i]为末尾的上升子序列是:

只包含a[i]的子序列

在满足j<i并且a[j]<a[i]的以a[j]为结尾的上升子序列追加上a[i]后得到的子序列

这二者之一。这样我们就能得到如下递推关系

dp[i]=max(1,dp[j]+1)  j<i并且a[j]<a[i]

使用这一递推公式可以在O(n^2)时间内解决问题

看代码

  1. #include<iostream>
  2. #include<stdio.h>
  3. #include<string.h>
  4. #include<cmath>
  5. #include<math.h>
  6. #include<algorithm>
  7. #include<set>
  8. #include<queue>
  9. typedef long long ll;
  10. using namespace std;
  11. const ll mod=1e9+;
  12. #define INF 0x3f3f3f
  13. int main()
  14. {
  15. int n,ans=;
  16. int a[];
  17. int dp[];
  18. memset(dp,,sizeof(dp));
  19. cin>>n;
  20. for(int i=;i<n;i++)
  21. {
  22. cin>>a[i];
  23. }
  24. for(int i=;i<n;i++)
  25. {
  26. dp[i]=;
  27. for(int j=;j<i;j++)
  28. {
  29. if(a[j]<a[i])
  30. dp[i]=max(dp[i],dp[j]+);
  31. }
  32. ans=max(ans,dp[i]);
  33. }
  34. cout<<ans<<endl;
  35. return ;
  36. }

此外还可以定义其它的递推关系。前面我们利用do求取针对最末尾的元素的最长的子序列。如果子序列

长度相同,那么最末尾的元素较小的在之后会更加有优势,所以我们反过来用dp针对长度相同情况下最小

的末尾元素进行求解

dp[i]:=长度为i+1的上升子序列中末尾元素的最小值

最开始全部dp[i]的值都初始化为为INF。然后由前到后逐个考虑数组的元素,对于每个a[j],如果i=0或者dp[i-1]<a[j]的话,就用dp[i]=min(dp[i],a[j])进行更新。这里如果看不懂就自己拿抄稿本走一遍,就一清二楚了。最终找出使得dp[i]<INF的最大i+1就是结果了。这个dp直接实现的话,能够与

前面的方法一样在O(n^2)的时间内给出结果,但是这一算法还可以进一步优化。 首先dp数组里除了INF之外是单调递增的,所以可以知道对于

每一个a[j]最多只需要一次更新。对于这次更新在什么位子,不必逐个遍历,可以利用二分搜索,这样就可以在O(nlogn)的时间内求出结果

复杂度O(nlogn)

  1. #include<iostream>
  2. #include<stdio.h>
  3. #include<string.h>
  4. #include<cmath>
  5. #include<math.h>
  6. #include<algorithm>
  7. #include<set>
  8. #include<queue>
  9. typedef long long ll;
  10. using namespace std;
  11. const ll mod=1e9+;
  12. #define INF 0x3f3f3f
  13. int main()
  14. {
  15. int n;
  16. int a[];
  17. int dp[];
  18. cin>>n;
  19. fill(dp,dp+n,INF);
  20. for(int i=;i<n;i++)
  21. {
  22. cin>>a[i];
  23. }
  24. for(int i=;i<n;i++)
  25. {
  26. *lower_bound(dp,dp+n,a[i])=a[i];
  27. }
  28. cout<<lower_bound(dp,dp+n,INF)-dp<<endl;
  29. return ;
  30. }

上面代码中使用了lower_bound这个STL函数。这个函数从已排好序的序列a中利用二分搜索找出满足a[i]>=k的最小的a[i]的指针。

多重背包(dp专题)的更多相关文章

  1. luogu||P1776||宝物筛选||多重背包||dp||二进制优化

    题目描述 终于,破解了千年的难题.小FF找到了王室的宝物室,里面堆满了无数价值连城的宝物……这下小FF可发财了,嘎嘎.但是这里的宝物实在是太多了,小FF的采集车似乎装不下那么多宝物.看来小FF只能含泪 ...

  2. POJ 1742 Coins 【多重背包DP】

    题意:有n种面额的硬币.面额.个数分别为A_i.C_i,求最多能搭配出几种不超过m的金额? 思路:dp[j]就是总数为j的价值是否已经有了这种方法,如果现在没有,那么我们就一个个硬币去尝试直到有,这种 ...

  3. 【bzoj1531】[POI2005]Bank notes 多重背包dp

    题目描述 Byteotian Bit Bank (BBB) 拥有一套先进的货币系统,这个系统一共有n种面值的硬币,面值分别为b1, b2,..., bn. 但是每种硬币有数量限制,现在我们想要凑出面值 ...

  4. DZY Loves Math II:多重背包dp+组合数

    Description Input 第一行,两个正整数 S 和 q,q 表示询问数量.接下来 q 行,每行一个正整数 n. Output 输出共 q 行,分别为每个询问的答案. Sample Inpu ...

  5. hdu 2844 Coins (多重背包+二进制优化)

    链接:http://acm.hdu.edu.cn/showproblem.php?pid=2844 思路:多重背包 , dp[i] ,容量为i的背包最多能凑到多少容量,如果dp[i] = i,那么代表 ...

  6. 【bzoj4182】Shopping 树的点分治+dfs序+背包dp

    题目描述 给出一棵 $n$ 个点的树,每个点有物品重量 $w$ .体积 $c$ 和数目 $d$ .要求选出一个连通子图,使得总体积不超过背包容量 $m$ ,且总重量最大.求这个最大总重量. 输入 输入 ...

  7. HDU - 1059 背包dp

    题目: 有两个小朋友想要平分一大堆糖果,但他们不知道如何平分需要你的帮助,由于没有spj我们只需回答能否平分即可. 糖果大小有6种分别是1.2.3.4.5.6,每种若干颗,现在需要知道能不能将这些糖果 ...

  8. 单调队列优化DP,多重背包

    单调队列优化DP:http://www.cnblogs.com/ka200812/archive/2012/07/11/2585950.html 单调队列优化多重背包:http://blog.csdn ...

  9. DP大作战——多重背包

    题目描述 在之前的上机中,零崎已经出过了01背包和完全背包,也介绍了使用-1初始化容量限定背包必须装满这种小技巧,接下来的背包问题相对有些难度,可以说是01背包和完全背包的进阶问题. 多重背包:物品可 ...

随机推荐

  1. Cloudera安装要点

    C方式是完全离线方式 https://www.cloudera.com/documentation/enterprise/5-10-x/topics/cm_ig_installing_configur ...

  2. Poj 1316 Self Numbers(水题)

    一.Description In 1949 the Indian mathematician D.R. Kaprekar discovered a class of numbers called se ...

  3. python oop培训文档里面的 正宗oop、多个函数间反复return传参、多个文件无限复制粘贴扣字、无效废物滑稽类4种方式的例子。(2)

    把文档里面说的几种范式发出来. 4种编程范式实现一个人吃喝拉撒长身体的代码.语法很简单,思想模拟了所有程序员写代码时候的代码规划设计想法. 0.我不反对复制粘贴的写法,可以百度搜索复制粘贴网上现有的, ...

  4. C++零碎知识点(一)

    1.sizeof用法总结 ①与strlen比较       strlen 计算字符数组的字符数,以"\0"为结束判断,但不包括.   sizeof 计算数据(数组.变量.类型.结构 ...

  5. Sharepoint 对于是否签出文件进行编辑区别

    在库设置----版本控制设置 一.需要签出才能编辑 例如需要对以上通用盒进行修改时,若在“使用资源管理器中打开”粘贴文件时会提示必须先签出项目 签出文件后,再粘贴文件到文档库中,可以选择签入的版本类型 ...

  6. Dexdump 无法正常反编译问题

    WIN环境下无法正常运行,提示Unable open XXX as zip 解决方案:使用APKTOOL + JD-GUI进行替代反编译

  7. Flask02 路由的书写、蓝图、利用蓝图实现url前缀、利用蓝图实现子域名、访问静态文件

    1 书写路由的两种方法 1.1 利用Flask实例对象的 add_url_rule 方法实现 该方法有一个必填参数,两个默认参数 · rule : 请求路径的规则 endpoint : 端点,默认值是 ...

  8. 我对PageRank的理解及R语言实现

    PageRank,网页排名,又称网页级别.Google左侧排名或佩奇排名,是一种由搜索引擎根据网页之间相互的超链接计算的技术,而作为网页排名的要素之一,以Google公司创办人拉里·佩奇(Larry ...

  9. supervisor启动worker源码分析-worker.clj

    supervisor通过调用sync-processes函数来启动worker,关于sync-processes函数的详细分析请参见"storm启动supervisor源码分析-superv ...

  10. 25. CTF综合靶机渗透(17)

    靶机链接 https://www.vulnhub.com/entry/the-ether-evilscience,212 运行环境 本靶机提供了VMware的镜像,从Vulnhub下载之后解压,运行v ...