[关闭]
@feiyangyang 2022-06-06T06:26:59.000000Z 字数 3900 阅读 335

采药题解

题解


A:

01背包的模板,直接往里套。

找准状态转移方程即可;

状态转移方程:

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

后来,我将状态方程简化了,(就如同消除代数式中的同类项一样)

得到:F[v]=max{f[v],F[v-c[i]]+w[i]}

最后将F[v]输出

(注意F数组初始化为0,不要写-1,)

代码粘上

  1. #include<stdio.h>
  2. int max(int a,int b)
  3. {
  4. if (a>b) return a;
  5. else return b;
  6. }
  7. int main()
  8. {
  9. int f[1000]={0},c[1000],w[1000];
  10. int n,v,i,j;
  11. scanf("%d%d",&v,&n);
  12. for(i=1;i<=n;i++)scanf("%d%d",&c[i],&w[i]);
  13. for(i=1;i<=n;i++)
  14. for(j=v;j>=c[i];j--)
  15. {
  16. f[j]=max(f[j],f[j-c[i]]+w[i]);
  17. }
  18. printf("%d ",f[v]);
  19. return 0;
  20. }

B:
动态规划->背包->01背包
01背包的特点:物品只有一件。想放就放,不想放就别放
思路(1 / 2):
1.定义二维数组 , f[i][j]表示采第i株药,花费时间j可以采到的草药的最大总价值。
2.输入采摘某株草药的时间和这株草药的价值。
3.判断是否采摘这株草药
1.不采摘(背包容量不够),则时间不变,当前采摘的药等于采摘的第i-1株药

f[i][j] = f[i - 1][j] ;

2.采摘(背包容量足够),那么当前可以获得的草药为采i-1株草药,时间为少采第i种药的时间j-ti[i]并且加上当前草药的价值pri[i]

f[i][j] = f[i - 1][j - ti[i]] + pri[i] ;

这里需要注意 : 如果当前采摘这株草药获得的价值比采摘i-1株草药的价值低的话也不摘。
所以需要比较。因此无论摘不摘这一株草药,一开始的价值都应该是采摘i-1株草药的价值

f[i][j] = max(f[i][j], f[i - 1][j - ti[i]] + pri[i]) ;

相当于初始化当前可以获得的价值为采摘i-1株草药的价值,只有背包容量足够采摘当前这一株草药的时候,才判断是否采摘当前的这一株草药。

f[i][j] = f[i - 1][j] ;
    if(j >= ti[i]) 
        f[i][j] = max(f[i][j], f[i - 1][j - ti[i]] + pri[i]) ;

4.输出答案

  1. #include<bits/stdc++.h>
  2. using namespace std ;
  3. int ti[1005] , pri[1005] ;
  4. int f[1005][1005] ;
  5. int main()
  6. {
  7. int t , m ;
  8. cin >> t >> m ;
  9. for(int i = 1 ; i <= m ; ++i)
  10. {
  11. cin >> ti[i] >> pri[i] ;
  12. for(int j = 1 ; j <= t ; ++j)
  13. {
  14. f[i][j] = f[i - 1][j] ;
  15. if(j >= ti[i])
  16. f[i][j] = max(f[i][j], f[i - 1][j - ti[i]] + pri[i]) ;
  17. }
  18. }
  19. cout << f[m][t] ;
  20. return 0 ;
  21. }

思路(2 / 2)
1.相比于第一种思路,最大的不同就是使用的是一维数组而不是二维数组,不记录i,只记录花费的时间j,其他的都与二维数组的思路没有什么太大的不同

  1. #include<bits/stdc++.h>
  2. using namespace std ;
  3. int ti[1005] , pri[1005] ;
  4. int f[1005] ;
  5. int main()
  6. {
  7. int t , m ;
  8. cin >> t >> m ;
  9. for(int i = 1 ; i <= m ; ++i)
  10. cin >> ti[i] >> pri[i] ;
  11. for(int i = 1 ; i <= m ; ++i)
  12. for(int j = t ; j >= 1 ; --j)
  13. if(j >= ti[i])
  14. f[j] = max(f[j] , f[j - ti[i]] + pri[i]) ;
  15. cout << f[t] ;
  16. return 0 ;
  17. }

C:

这道题的解法很多,大部分是动态规划和记忆化搜索,但是如果T一旦很大的话,动态规划和记忆化搜索会被卡掉。那我们有没有别的方法呢?是有的。

下面隆重介绍:

启发式搜索

启发式搜索是在暴力DFS基础上进行剪枝的搜索。你可能会说,不就是剪了一点枝吗,会快很多吗?答案是会。

我们先说一下,所有的启发式搜索都会有一个估价函数。下面是这一题的估价函数。

  1. struct node
  2. {
  3. int c,v;
  4. double cost;
  5. };
  6. const int N=10000;
  7. node a[N+5];
  8. inline int f(int t,int v)
  9. {
  10. int tot=0;
  11. for(int i=1;t+i<=n;i++)
  12. {
  13. if(v>=a[t+i].c)
  14. {
  15. v-=a[t+i].c;
  16. tot+=a[t+i].v;
  17. }
  18. else return (int) (tot+v*a[t+i].cost);
  19. }
  20. return tot;
  21. }

那么估价函数是什么呢?我们都知道,背包问题的最基础的思路是O(2^n)。一个状态有两种决策,分别是取和不取,那么,我们在取的时候判断一下是不是超过了体积,在不取的时候判断一下,如果,我不取这个,那么,剩下的所有的价值+现有的价值有没有大于我所找到的目前的最优解,如果没有,不取是没有意义的。听不懂?没关系。我们先上核心代码。

  1. void DFS(int now,int cv,int cp)
  2. {
  3. ans=code::max(ans,cp);
  4. if(now>n) {return;}
  5. if(f(now,cv)+cp>ans) DFS(now+1,cv,cp);
  6. if(cv-a[now].c>=0) DFS(now+1,cv-a[now].c,cp+a[now].v);
  7. }

假设,我现在的最优解是2,如果我不取这个物品,所能得到的估价比这个小,说明不取是没有意义的,因为就算你剩下的全部取了都不是最优解。那么,估价有没有可能估小了呢?不可能。

  1. bool operator <(const node &a,const node &b)
  2. {
  3. return a.cost>b.cost;
  4. }
  5. sort(a+1,a+1+n);

上面这段代码就保证了不可能估小。以及,我们在估价时,不管它到底可不可以放进去,只有背包有空间,就放,而放的价值就是单位价值:

a[i].cost=1.0*a[i].v/a[i].c

好的,我们现在就放出完整代码。
代码均由本人实现

  1. #include <algorithm>
  2. #include <cctype>
  3. #include <stdio.h>
  4. int n, tot, ans;
  5. struct node
  6. {
  7. int c, v;
  8. double cost;
  9. };
  10. using namespace std;
  11. const int N = 10000;
  12. node a[N + 5];
  13. int read() /*快读大法吼啊!*/
  14. {
  15. int x = 0;short w = 0;char ch = 0;
  16. while (!isdigit(ch)){w |= ch == '-';ch = getchar();}
  17. while (isdigit(ch)){x = (x << 3) + (x << 1) + (ch ^ 48);ch = getchar();}
  18. return w ? -x : x;
  19. }
  20. inline int f(int t, int v) //估价函数,解释见上。
  21. {
  22. int tot = 0;
  23. for (int i = 1; t + i <= n; i++)
  24. {
  25. if (v >= a[t + i].c)
  26. {
  27. v -= a[t + i].c;
  28. tot += a[t + i].v;
  29. }
  30. else
  31. return (int)(tot + v * a[t + i].cost);
  32. }
  33. return tot;
  34. }
  35. bool operator<(const node &a, const node &b)
  36. {
  37. return a.cost > b.cost;
  38. } /*等价于
  39. bool cmp(node a,node b)
  40. {
  41. return a.cost>b.cost;
  42. }
  43. */
  44. void DFS(int now, int cv, int cp)
  45. {
  46. ans = max(ans, cp);
  47. if (now > n)
  48. {
  49. return;
  50. }
  51. if (f(now, cv) + cp > ans)
  52. DFS(now + 1, cv, cp);
  53. if (cv - a[now].c >= 0)
  54. DFS(now + 1, cv - a[now].c, cp + a[now].v);
  55. }
  56. int main()
  57. {
  58. tot = read(), n = read();
  59. for (int i = 1; i <= n; i++)
  60. {
  61. a[i].c = read(), a[i].v = read();
  62. a[i].cost = 1.0 * a[i].v / a[i].c;
  63. }
  64. sort(a + 1, a + 1 + n);
  65. DFS(0, tot, 0);
  66. printf("%d", ans);
  67. return 0;
  68. }

D:

  1. #include<iostream>
  2. #include<cmath>//头文件
  3. using namespace std;
  4. int f[105][1005];//数组一定要多开几个,不然WA
  5. int m,n,mi[105],vi[105];
  6. int dfs(int x,int sumW)//把一个参数作为该函数的函数类型(优化一)
  7. {
  8. if(sumW>m)return -10000;//时间不能超出欧!
  9. if(f[x][sumW]) return/*(优化二)*/ f[x][sumW];//用一个数组来储存每一次dfs的数值,避免重复
  10. if(x==n+1) return 0;//到末尾了就返回0
  11. f[x][sumW]=max(dfs(x+1,sumW+mi[x])+vi[x],dfs(x+1,sumW));//取最大值,没啥好说的
  12. return f[x][sumW];//输出最大值
  13. }
  14. int main(){
  15. cin>>m>>n;
  16. for(int i=0;i<n;i++){
  17. cin>>mi[i]>>vi[i];
  18. }
  19. cout<<dfs(0,0)<<endl;
  20. return 0;
  21. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注