当前位置:   article > 正文

戳气球问题_戳气球 算法

戳气球 算法

戳气球问题本质上可以认为是一种组合+逆推的问题(背包问题模型)

 我们可以有两种方法

1.dfs爆搜法

2.dp法

1.dfs爆搜法

dfs爆搜就是暴力枚举出哪个先破,哪个后破,直到找出最佳的方案

本质上是一个全排列问题,但是全排列问题时间复杂度是O(10的n次方),不适合本题,所以我们采用dp更好

  1. #include <iostream>
  2. using namespace std;
  3. int n;
  4. int ans = 0;
  5. int w[1001];
  6. int visit[1002];
  7. void dfs(int x, int sum)
  8. {//x表示选了几个,sum表示现在的奖励总和
  9. if (x >= n)
  10. {
  11. ans = max(ans, sum);
  12. return;
  13. }
  14. for (int i = 1;i <= n;i++)
  15. {
  16. if (!visit[i])
  17. {
  18. visit[i] = 1;
  19. int L=1, R=2;
  20. for (int k = i - 1;k >= 0;k--)
  21. if (!visit[k])
  22. {
  23. L = k; break;
  24. }
  25. for (int k = i + 1;k <= n+1;k++)
  26. if (!visit[k])
  27. {
  28. R = k; break;
  29. }
  30. dfs(x + 1, sum + w[i]*w[L]*w[R]);
  31. visit[i] = 0;
  32. }
  33. }
  34. }
  35. int main()
  36. {
  37. cin >> n;
  38. for (int i = 1;i <= n;i++)
  39. cin >> w[i];
  40. w[0] = w[n + 1] = 1;
  41. //先戳破第一个
  42. for (int i = 1;i <= n;i++)
  43. {
  44. if (!visit[i])
  45. {
  46. visit[i] = 1;
  47. int L=0, R=n+1;
  48. for (int k = i - 1;k >= 0;k--)
  49. if (!visit[k])
  50. {
  51. L = k; break;
  52. }
  53. for (int k = i + 1;k <=n+1;k++)
  54. if (!visit[k])
  55. {
  56. R = k; break;
  57. }
  58. dfs(1, w[i]*w[L]*w[R]);
  59. visit[i] = 0;
  60. }
  61. }
  62. cout << ans;
  63. }

2.dp法

我们可以先假设两个虚拟节点,第0结点和第n+1结点,他们的奖励都是1

下面分析一下假设k是最后一个被戳破的气球

 在(i,k)区间中,由于全部被戳破,他的最大奖励是dp[i][k]

在(k,j)区间中,由于全部被戳破,他的最大奖励是dp[k][j]

此时k的左节点是  第i个结点,k的右节点是  第j个结点

 

那么可以得到一个基本框架

  1. for(遍历所有i)
  2. for(遍历所有j)
  3. for(i<k<j)
  4. dp[i][j]=max(dp[i][j],dp[i][k]+dp[k][j]+w[i]*w[k]*w[j]);

 最后的答案就是dp[0][n+1],表示0~n+1区间获得的最大奖励值

所以我们只要确保计算dp[i][j]的时候,dp[i][k]和dp[k][j]已经计算出来了即可

所以i是从下往上遍历的,j是从左往右遍历的

 下面给出代码

  1. #include <iostream>
  2. using namespace std;
  3. const int N = 1005;
  4. int dp[N][N];
  5. int s[N];
  6. int main()
  7. {
  8. int n;
  9. cin >> n;
  10. for (int i = 1;i <= n;i++)
  11. {
  12. cin >> s[i];
  13. }
  14. s[0] = s[n + 1] = 1;
  15. //两个虚拟结点
  16. //dp[i][j]表示开区间(i,j)的最大奖励
  17. for (int i = n - 1;i >= 0;i--) //i从倒数第二个开始,遍历到0
  18. for (int j = i + 2;j <= n + 1;j++) //由于开区间,(i,j)至少应包含一个,遍历到n+1
  19. {
  20. for (int k = i + 1;k < j;k++) //开区间,k表示i的下一个,k不会到达j,因为开区间,j取不到
  21. dp[i][j] = max(dp[i][j], dp[i][k] + dp[k][j] + s[i] * s[j] * s[k]);
  22. }
  23. cout << dp[0][n + 1];
  24. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/717712
推荐阅读
相关标签
  

闽ICP备14008679号